blob: a8d4ab82466700f804aae5a6f74ee4ce09b02ee2 [file] [log] [blame]
Skyler Greyda16adf2023-03-05 10:22:12 +00001import { LinkWarningFooter } from "./../../utils/defaults.js";
pineafan1e462ab2023-03-07 21:34:06 +00002import {
3 ActionRowBuilder,
4 ButtonBuilder,
5 CommandInteraction,
6 GuildMember,
7 ButtonStyle,
8 Message,
9 ButtonInteraction,
10 ModalBuilder,
11 TextInputBuilder,
12 TextInputStyle
13} from "discord.js";
TheCodedProff86ba092023-01-27 17:10:07 -050014import type { SlashCommandSubcommandBuilder } from "discord.js";
pineafan32767212022-03-14 21:27:39 +000015import confirmationMessage from "../../utils/confirmationMessage.js";
pineafan4edb7762022-06-26 19:21:04 +010016import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
pineafan32767212022-03-14 21:27:39 +000017import keyValueList from "../../utils/generateKeyValueList.js";
pineafan63fc5e22022-08-04 22:04:10 +010018import client from "../../utils/client.js";
PineaFane6ba7882023-01-18 20:41:16 +000019import { areTicketsEnabled, create } from "../../actions/createModActionTicket.js";
TheCodedProf94ff6de2023-02-22 17:47:26 -050020import getEmojiByName from "../../utils/getEmojiByName.js";
pineafan32767212022-03-14 21:27:39 +000021
Skyler Greyda16adf2023-03-05 10:22:12 +000022const command = (builder: SlashCommandSubcommandBuilder) =>
23 builder
24 .setName("nick")
25 .setDescription("Changes a users nickname")
26 .addUserOption((option) => option.setName("user").setDescription("The user to change").setRequired(true))
27 .addStringOption((option) =>
28 option.setName("name").setDescription("The name to set | Leave blank to clear").setRequired(false)
29 );
pineafan32767212022-03-14 21:27:39 +000030
pineafan1e462ab2023-03-07 21:34:06 +000031const callback = async (
32 interaction: CommandInteraction | ButtonInteraction,
33 member?: GuildMember
34): Promise<unknown> => {
PineaFane6ba7882023-01-18 20:41:16 +000035 const { log, NucleusColors, entry, renderDelta, renderUser } = client.logger;
pineafan6de4da52023-03-07 20:43:44 +000036 let newNickname;
37 if (!interaction.isButton()) {
38 member = interaction.options.getMember("user") as GuildMember;
39 newNickname = interaction.options.get("name")?.value as string | undefined;
40 }
41 if (!member) return;
pineafan377794f2022-04-18 19:01:01 +010042 // TODO:[Modals] Replace this with a modal
pineafan6de4da52023-03-07 20:43:44 +000043 let notify = false;
pineafan02ba0232022-07-24 22:16:15 +010044 let confirmation;
Skyler Greyad002172022-08-16 18:48:26 +010045 let timedOut = false;
46 let success = false;
PineaFane6ba7882023-01-18 20:41:16 +000047 let createAppealTicket = false;
pineafan6de4da52023-03-07 20:43:44 +000048 let firstRun = !interaction.isButton();
PineaFane6ba7882023-01-18 20:41:16 +000049 do {
pineafan02ba0232022-07-24 22:16:15 +010050 confirmation = await new confirmationMessage(interaction)
51 .setEmoji("PUNISH.NICKNAME.RED")
52 .setTitle("Nickname")
Skyler Grey75ea9172022-08-06 10:22:23 +010053 .setDescription(
54 keyValueList({
pineafan6de4da52023-03-07 20:43:44 +000055 user: renderUser(member.user),
pineafan1e462ab2023-03-07 21:34:06 +000056 "new nickname": `${newNickname ? newNickname : "*No nickname*"}`
57 }) + `Are you sure you want to ${newNickname ? "change" : "clear"} <@!${member.id}>'s nickname?`
Skyler Grey75ea9172022-08-06 10:22:23 +010058 )
pineafan02ba0232022-07-24 22:16:15 +010059 .setColor("Danger")
Skyler Grey75ea9172022-08-06 10:22:23 +010060 .addCustomBoolean(
PineaFane6ba7882023-01-18 20:41:16 +000061 "appeal",
62 "Create appeal ticket",
63 !(await areTicketsEnabled(interaction.guild!.id)),
pineafan1e462ab2023-03-07 21:34:06 +000064 async () => await create(interaction.guild!, member!.user, interaction.user, "Nickname changed"),
PineaFane6ba7882023-01-18 20:41:16 +000065 "An appeal ticket will be created",
66 null,
67 "CONTROL.TICKET",
68 createAppealTicket
69 )
70 .addCustomBoolean(
Skyler Grey75ea9172022-08-06 10:22:23 +010071 "notify",
72 "Notify user",
73 false,
74 null,
PineaFane6ba7882023-01-18 20:41:16 +000075 "The user will be sent a DM",
PineaFana34d04b2023-01-03 22:05:42 +000076 null,
Skyler Grey75ea9172022-08-06 10:22:23 +010077 "ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
78 notify
79 )
pineafan6de4da52023-03-07 20:43:44 +000080 .addModal(
81 "Change nickname",
82 "ICONS.EDIT",
83 "modal",
84 newNickname ?? "",
pineafan1e462ab2023-03-07 21:34:06 +000085 new ModalBuilder().setTitle("Editing nickname").addComponents(
86 new ActionRowBuilder<TextInputBuilder>().addComponents(
87 new TextInputBuilder()
88 .setCustomId("default")
89 .setLabel("Nickname")
90 .setMaxLength(32)
91 .setRequired(false)
92 .setStyle(TextInputStyle.Short)
93 .setValue(newNickname ? newNickname : " ")
94 )
pineafan6de4da52023-03-07 20:43:44 +000095 )
96 )
PineaFane6ba7882023-01-18 20:41:16 +000097 .setFailedMessage("No changes were made", "Success", "PUNISH.NICKNAME.GREEN")
98 .send(!firstRun);
99 firstRun = false;
Skyler Greyad002172022-08-16 18:48:26 +0100100 if (confirmation.cancelled) timedOut = true;
PineaFane6ba7882023-01-18 20:41:16 +0000101 else if (confirmation.success !== undefined) success = true;
Skyler Greyad002172022-08-16 18:48:26 +0100102 else if (confirmation.components) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000103 notify = confirmation.components["notify"]!.active;
PineaFane6ba7882023-01-18 20:41:16 +0000104 createAppealTicket = confirmation.components["appeal"]!.active;
pineafan02ba0232022-07-24 22:16:15 +0100105 }
pineafan1e462ab2023-03-07 21:34:06 +0000106 if (confirmation.modals) newNickname = confirmation.modals![0]!.value;
PineaFane6ba7882023-01-18 20:41:16 +0000107 } while (!timedOut && !success);
108 if (timedOut || !success) return;
109 let dmSent = false;
110 let dmMessage: Message;
111 const config = await client.database.guilds.read(interaction.guild!.id);
Skyler Greyad002172022-08-16 18:48:26 +0100112 try {
113 if (notify) {
PineaFane6ba7882023-01-18 20:41:16 +0000114 const messageData: {
115 embeds: EmojiEmbed[];
116 components: ActionRowBuilder<ButtonBuilder>[];
117 } = {
Skyler Greyad002172022-08-16 18:48:26 +0100118 embeds: [
119 new EmojiEmbed()
120 .setEmoji("PUNISH.NICKNAME.RED")
121 .setTitle("Nickname changed")
122 .setDescription(
pineafan1e462ab2023-03-07 21:34:06 +0000123 `Your nickname was ${newNickname ? "changed" : "cleared"} in ${interaction.guild!.name}.` +
124 (newNickname ? `\nIt is now: ${newNickname}` : "") +
Skyler Greyad002172022-08-16 18:48:26 +0100125 "\n\n" +
PineaFane6ba7882023-01-18 20:41:16 +0000126 (createAppealTicket
Skyler Greyda16adf2023-03-05 10:22:12 +0000127 ? `You can appeal this in the ticket created in <#${
128 confirmation.components!["appeal"]!.response
129 }>`
Skyler Greyad002172022-08-16 18:48:26 +0100130 : "")
131 )
132 .setStatus("Danger")
Skyler Greyda16adf2023-03-05 10:22:12 +0000133 ],
134 components: []
PineaFane6ba7882023-01-18 20:41:16 +0000135 };
136 if (config.moderation.nick.text && config.moderation.nick.link) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000137 messageData.embeds[0]!.setFooter(LinkWarningFooter);
138 messageData.components.push(
139 new ActionRowBuilder<ButtonBuilder>().addComponents(
140 new ButtonBuilder()
PineaFane6ba7882023-01-18 20:41:16 +0000141 .setStyle(ButtonStyle.Link)
142 .setLabel(config.moderation.nick.text)
pineafan1e462ab2023-03-07 21:34:06 +0000143 .setURL(config.moderation.nick.link.replaceAll("{id}", member.id))
Skyler Greyda16adf2023-03-05 10:22:12 +0000144 )
145 );
PineaFane6ba7882023-01-18 20:41:16 +0000146 }
pineafan6de4da52023-03-07 20:43:44 +0000147 dmMessage = await member.send(messageData);
PineaFane6ba7882023-01-18 20:41:16 +0000148 dmSent = true;
Skyler Greyad002172022-08-16 18:48:26 +0100149 }
150 } catch {
PineaFane6ba7882023-01-18 20:41:16 +0000151 dmSent = false;
Skyler Greyad002172022-08-16 18:48:26 +0100152 }
PineaFane6ba7882023-01-18 20:41:16 +0000153 let before: string | null;
Skyler Greyad002172022-08-16 18:48:26 +0100154 try {
PineaFane6ba7882023-01-18 20:41:16 +0000155 before = member.nickname;
Skyler Greyf4f21c42023-03-08 14:36:29 +0000156 await member.setNickname(newNickname ?? null, "Nucleus Nickname command");
Skyler Greyad002172022-08-16 18:48:26 +0100157 await client.database.history.create(
158 "nickname",
PineaFane6ba7882023-01-18 20:41:16 +0000159 interaction.guild!.id,
Skyler Greyad002172022-08-16 18:48:26 +0100160 member.user,
161 interaction.user,
162 null,
163 before,
pineafan6de4da52023-03-07 20:43:44 +0000164 newNickname
Skyler Greyad002172022-08-16 18:48:26 +0100165 );
Skyler Greyad002172022-08-16 18:48:26 +0100166 } catch {
167 await interaction.editReply({
168 embeds: [
169 new EmojiEmbed()
170 .setEmoji("PUNISH.NICKNAME.RED")
171 .setTitle("Nickname")
172 .setDescription("Something went wrong and the users nickname could not be changed.")
173 .setStatus("Danger")
174 ],
175 components: []
176 });
PineaFane6ba7882023-01-18 20:41:16 +0000177 if (dmSent) await dmMessage!.delete();
Skyler Greyad002172022-08-16 18:48:26 +0100178 return;
179 }
PineaFane6ba7882023-01-18 20:41:16 +0000180 const data = {
181 meta: {
182 type: "memberUpdate",
183 displayName: "Member Updated",
184 calculateType: "guildMemberUpdate",
185 color: NucleusColors.yellow,
186 emoji: "PUNISH.NICKNAME.YELLOW",
TheCodedProf6ec331b2023-02-20 12:13:06 -0500187 timestamp: Date.now()
PineaFane6ba7882023-01-18 20:41:16 +0000188 },
189 list: {
pineafan6de4da52023-03-07 20:43:44 +0000190 member: entry(member.id, renderUser(member.user)),
PineaFane6ba7882023-01-18 20:41:16 +0000191 before: entry(before, before ?? "*No nickname set*"),
pineafan6de4da52023-03-07 20:43:44 +0000192 after: entry(newNickname ?? null, newNickname ?? "*No nickname set*"),
TheCodedProf6ec331b2023-02-20 12:13:06 -0500193 updated: entry(Date.now(), renderDelta(Date.now())),
PineaFane6ba7882023-01-18 20:41:16 +0000194 updatedBy: entry(interaction.user.id, renderUser(interaction.user))
195 },
TheCodedProf94ff6de2023-02-22 17:47:26 -0500196 separate: {
Skyler Greyda16adf2023-03-05 10:22:12 +0000197 end:
198 getEmojiByName("ICONS.NOTIFY." + (notify ? "ON" : "OFF")) +
199 ` The user was ${notify ? "" : "not "}notified`
TheCodedProf94ff6de2023-02-22 17:47:26 -0500200 },
PineaFane6ba7882023-01-18 20:41:16 +0000201 hidden: {
202 guild: interaction.guild!.id
203 }
204 };
Skyler Greyf4f21c42023-03-08 14:36:29 +0000205 await log(data);
PineaFane6ba7882023-01-18 20:41:16 +0000206 const failed = !dmSent && notify;
Skyler Greyad002172022-08-16 18:48:26 +0100207 await interaction.editReply({
208 embeds: [
209 new EmojiEmbed()
210 .setEmoji(`PUNISH.NICKNAME.${failed ? "YELLOW" : "GREEN"}`)
211 .setTitle("Nickname")
212 .setDescription(
213 "The members nickname was changed" +
214 (failed ? ", but was not notified" : "") +
PineaFane6ba7882023-01-18 20:41:16 +0000215 (confirmation.components!["appeal"]!.response !== null
216 ? ` and an appeal ticket was opened in <#${confirmation.components!["appeal"]!.response}>`
Skyler Greyad002172022-08-16 18:48:26 +0100217 : "")
218 )
219 .setStatus(failed ? "Warning" : "Success")
220 ],
221 components: []
222 });
pineafan63fc5e22022-08-04 22:04:10 +0100223};
pineafan32767212022-03-14 21:27:39 +0000224
pineafan6de4da52023-03-07 20:43:44 +0000225const check = async (interaction: CommandInteraction | ButtonInteraction, partial: boolean, target?: GuildMember) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100226 const member = interaction.member as GuildMember;
TheCodedProff86ba092023-01-27 17:10:07 -0500227 // Check if the user has manage_nicknames permission
228 if (!member.permissions.has("ManageNicknames")) return "You do not have the *Manage Nicknames* permission";
229 if (partial) return true;
PineaFan1dee28f2023-01-16 22:09:07 +0000230 const me = interaction.guild!.members.me!;
pineafan6de4da52023-03-07 20:43:44 +0000231 let apply: GuildMember;
232 if (interaction.isButton()) {
233 apply = target!;
234 } else {
235 apply = interaction.options.getMember("user") as GuildMember;
236 }
pineafan62ce1922022-08-25 20:34:45 +0100237 const memberPos = member.roles.cache.size ? member.roles.highest.position : 0;
238 const mePos = me.roles.cache.size ? me.roles.highest.position : 0;
239 const applyPos = apply.roles.cache.size ? apply.roles.highest.position : 0;
PineaFan1dee28f2023-01-16 22:09:07 +0000240 if (!interaction.guild) return false;
pineafanc1c18792022-08-03 21:41:36 +0100241 // Do not allow any changing of the owner
PineaFan0d06edc2023-01-17 22:10:31 +0000242 if (member.id === interaction.guild.ownerId) return "You cannot change the owner's nickname";
pineafan377794f2022-04-18 19:01:01 +0100243 // Check if Nucleus can change the nickname
TheCodedProf0941da42023-02-18 20:28:04 -0500244 if (!(mePos > applyPos)) return `I do not have a role higher than <@${apply.id}>`;
pineafan377794f2022-04-18 19:01:01 +0100245 // Check if Nucleus has permission to change the nickname
PineaFan0d06edc2023-01-17 22:10:31 +0000246 if (!me.permissions.has("ManageNicknames")) return "I do not have the *Manage Nicknames* permission";
pineafan377794f2022-04-18 19:01:01 +0100247 // Allow the owner to change anyone's nickname
pineafan63fc5e22022-08-04 22:04:10 +0100248 if (member.id === interaction.guild.ownerId) return true;
pineafane625d782022-05-09 18:04:32 +0100249 // Allow changing your own nickname
pineafan63fc5e22022-08-04 22:04:10 +0100250 if (member === apply) return true;
pineafan377794f2022-04-18 19:01:01 +0100251 // Check if the user is below on the role list
TheCodedProf0941da42023-02-18 20:28:04 -0500252 if (!(memberPos > applyPos)) return `You do not have a role higher than <@${apply.id}>`;
pineafan377794f2022-04-18 19:01:01 +0100253 // Allow change
pineafan63fc5e22022-08-04 22:04:10 +0100254 return true;
255};
pineafan32767212022-03-14 21:27:39 +0000256
Skyler Grey75ea9172022-08-06 10:22:23 +0100257export { command, callback, check };
TheCodedProfa112f612023-01-28 18:06:45 -0500258export const metadata = {
Skyler Greyda16adf2023-03-05 10:22:12 +0000259 longDescription:
260 "Changes the nickname of a member. This is the name that shows in the member list and on messages.",
261 premiumOnly: true
262};