blob: 9d5aa3aeb36a050672855aa888b8d03995284b53 [file] [log] [blame]
PineaFane6ba7882023-01-18 20:41:16 +00001import { LinkWarningFooter } from './../../utils/defaults.js';
2import { ActionRowBuilder, ButtonBuilder, CommandInteraction, GuildMember, ButtonStyle, Message } 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";
pineafan32767212022-03-14 21:27:39 +00009
PineaFan64486c42022-12-28 09:21:04 +000010
11const command = (builder: SlashCommandSubcommandBuilder) => builder
12 .setName("nick")
PineaFan538d3752023-01-12 21:48:23 +000013 // .setNameLocalizations({"ru": "name", "zh-CN": "nickname"})
PineaFan64486c42022-12-28 09:21:04 +000014 .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 );
19
pineafan32767212022-03-14 21:27:39 +000020
pineafan3a02ea32022-08-11 21:35:04 +010021const callback = async (interaction: CommandInteraction): Promise<unknown> => {
PineaFane6ba7882023-01-18 20:41:16 +000022 const { log, NucleusColors, entry, renderDelta, renderUser } = client.logger;
pineafan377794f2022-04-18 19:01:01 +010023 // TODO:[Modals] Replace this with a modal
pineafan02ba0232022-07-24 22:16:15 +010024 let notify = true;
25 let confirmation;
Skyler Greyad002172022-08-16 18:48:26 +010026 let timedOut = false;
27 let success = false;
PineaFane6ba7882023-01-18 20:41:16 +000028 let createAppealTicket = false;
29 let firstRun = true;
30 do {
pineafan02ba0232022-07-24 22:16:15 +010031 confirmation = await new confirmationMessage(interaction)
32 .setEmoji("PUNISH.NICKNAME.RED")
33 .setTitle("Nickname")
Skyler Grey75ea9172022-08-06 10:22:23 +010034 .setDescription(
35 keyValueList({
PineaFane6ba7882023-01-18 20:41:16 +000036 user: renderUser(interaction.options.getUser("user")!),
Skyler Grey75ea9172022-08-06 10:22:23 +010037 "new nickname": `${
PineaFane6ba7882023-01-18 20:41:16 +000038 interaction.options.get("name")?.value as string ? interaction.options.get("name")?.value as string : "*No nickname*"
Skyler Grey75ea9172022-08-06 10:22:23 +010039 }`
40 }) +
PineaFane6ba7882023-01-18 20:41:16 +000041 `Are you sure you want to ${interaction.options.get("name")?.value as string ? "change" : "clear"} <@!${
Skyler Grey11236ba2022-08-08 21:13:33 +010042 (interaction.options.getMember("user") as GuildMember).id
Skyler Grey75ea9172022-08-06 10:22:23 +010043 }>'s nickname?`
44 )
pineafan02ba0232022-07-24 22:16:15 +010045 .setColor("Danger")
Skyler Grey75ea9172022-08-06 10:22:23 +010046 .addCustomBoolean(
PineaFane6ba7882023-01-18 20:41:16 +000047 "appeal",
48 "Create appeal ticket",
49 !(await areTicketsEnabled(interaction.guild!.id)),
50 async () => await create(interaction.guild!, interaction.options.getUser("user")!, interaction.user, "Nickname changed"),
51 "An appeal ticket will be created",
52 null,
53 "CONTROL.TICKET",
54 createAppealTicket
55 )
56 .addCustomBoolean(
Skyler Grey75ea9172022-08-06 10:22:23 +010057 "notify",
58 "Notify user",
59 false,
60 null,
PineaFane6ba7882023-01-18 20:41:16 +000061 "The user will be sent a DM",
PineaFana34d04b2023-01-03 22:05:42 +000062 null,
Skyler Grey75ea9172022-08-06 10:22:23 +010063 "ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
64 notify
65 )
PineaFane6ba7882023-01-18 20:41:16 +000066 .setFailedMessage("No changes were made", "Success", "PUNISH.NICKNAME.GREEN")
67 .send(!firstRun);
68 firstRun = false;
Skyler Greyad002172022-08-16 18:48:26 +010069 if (confirmation.cancelled) timedOut = true;
PineaFane6ba7882023-01-18 20:41:16 +000070 else if (confirmation.success !== undefined) success = true;
Skyler Greyad002172022-08-16 18:48:26 +010071 else if (confirmation.components) {
PineaFane6ba7882023-01-18 20:41:16 +000072 notify = confirmation.components['notify']!.active;
73 createAppealTicket = confirmation.components["appeal"]!.active;
pineafan02ba0232022-07-24 22:16:15 +010074 }
PineaFane6ba7882023-01-18 20:41:16 +000075 } while (!timedOut && !success);
76 if (timedOut || !success) return;
77 let dmSent = false;
78 let dmMessage: Message;
79 const config = await client.database.guilds.read(interaction.guild!.id);
Skyler Greyad002172022-08-16 18:48:26 +010080 try {
81 if (notify) {
PineaFane6ba7882023-01-18 20:41:16 +000082 const messageData: {
83 embeds: EmojiEmbed[];
84 components: ActionRowBuilder<ButtonBuilder>[];
85 } = {
Skyler Greyad002172022-08-16 18:48:26 +010086 embeds: [
87 new EmojiEmbed()
88 .setEmoji("PUNISH.NICKNAME.RED")
89 .setTitle("Nickname changed")
90 .setDescription(
PineaFane6ba7882023-01-18 20:41:16 +000091 `Your nickname was ${interaction.options.get("name")?.value as string ? "changed" : "cleared"} in ${
92 interaction.guild!.name
Skyler Greyad002172022-08-16 18:48:26 +010093 }.` +
PineaFane6ba7882023-01-18 20:41:16 +000094 (interaction.options.get("name")?.value as string
95 ? ` it is now: ${interaction.options.get("name")?.value as string}`
Skyler Greyad002172022-08-16 18:48:26 +010096 : "") +
97 "\n\n" +
PineaFane6ba7882023-01-18 20:41:16 +000098 (createAppealTicket
99 ? `You can appeal this in the ticket created in <#${confirmation.components!["appeal"]!.response}>`
Skyler Greyad002172022-08-16 18:48:26 +0100100 : "")
101 )
102 .setStatus("Danger")
PineaFane6ba7882023-01-18 20:41:16 +0000103 ], components: []
104 };
105 if (config.moderation.nick.text && config.moderation.nick.link) {
106 messageData.embeds[0]!.setFooter(LinkWarningFooter)
107 messageData.components.push(new ActionRowBuilder<ButtonBuilder>()
108 .addComponents(new ButtonBuilder()
109 .setStyle(ButtonStyle.Link)
110 .setLabel(config.moderation.nick.text)
111 .setURL(config.moderation.nick.link.replaceAll("{id}", (interaction.options.getMember("user") as GuildMember).id))
112 )
113 )
114 }
115 dmMessage = await (interaction.options.getMember("user") as GuildMember).send(messageData);
116 dmSent = true;
Skyler Greyad002172022-08-16 18:48:26 +0100117 }
118 } catch {
PineaFane6ba7882023-01-18 20:41:16 +0000119 dmSent = false;
Skyler Greyad002172022-08-16 18:48:26 +0100120 }
PineaFane6ba7882023-01-18 20:41:16 +0000121 let member: GuildMember;
122 let before: string | null;
123 let nickname: string | undefined;
Skyler Greyad002172022-08-16 18:48:26 +0100124 try {
PineaFane6ba7882023-01-18 20:41:16 +0000125 member = interaction.options.getMember("user") as GuildMember;
126 before = member.nickname;
127 nickname = interaction.options.get("name")?.value as string | undefined;
Skyler Greyad002172022-08-16 18:48:26 +0100128 member.setNickname(nickname ?? null, "Nucleus Nickname command");
129 await client.database.history.create(
130 "nickname",
PineaFane6ba7882023-01-18 20:41:16 +0000131 interaction.guild!.id,
Skyler Greyad002172022-08-16 18:48:26 +0100132 member.user,
133 interaction.user,
134 null,
135 before,
136 nickname
137 );
Skyler Greyad002172022-08-16 18:48:26 +0100138 } catch {
139 await interaction.editReply({
140 embeds: [
141 new EmojiEmbed()
142 .setEmoji("PUNISH.NICKNAME.RED")
143 .setTitle("Nickname")
144 .setDescription("Something went wrong and the users nickname could not be changed.")
145 .setStatus("Danger")
146 ],
147 components: []
148 });
PineaFane6ba7882023-01-18 20:41:16 +0000149 if (dmSent) await dmMessage!.delete();
Skyler Greyad002172022-08-16 18:48:26 +0100150 return;
151 }
PineaFane6ba7882023-01-18 20:41:16 +0000152 const data = {
153 meta: {
154 type: "memberUpdate",
155 displayName: "Member Updated",
156 calculateType: "guildMemberUpdate",
157 color: NucleusColors.yellow,
158 emoji: "PUNISH.NICKNAME.YELLOW",
159 timestamp: new Date().getTime()
160 },
161 list: {
162 memberId: entry(member.id, `\`${member.id}\``),
163 before: entry(before, before ?? "*No nickname set*"),
164 after: entry(nickname ?? null, nickname ?? "*No nickname set*"),
165 updated: entry(new Date().getTime(), renderDelta(new Date().getTime())),
166 updatedBy: entry(interaction.user.id, renderUser(interaction.user))
167 },
168 hidden: {
169 guild: interaction.guild!.id
170 }
171 };
172 log(data);
173 const failed = !dmSent && notify;
Skyler Greyad002172022-08-16 18:48:26 +0100174 await interaction.editReply({
175 embeds: [
176 new EmojiEmbed()
177 .setEmoji(`PUNISH.NICKNAME.${failed ? "YELLOW" : "GREEN"}`)
178 .setTitle("Nickname")
179 .setDescription(
180 "The members nickname was changed" +
181 (failed ? ", but was not notified" : "") +
PineaFane6ba7882023-01-18 20:41:16 +0000182 (confirmation.components!["appeal"]!.response !== null
183 ? ` and an appeal ticket was opened in <#${confirmation.components!["appeal"]!.response}>`
Skyler Greyad002172022-08-16 18:48:26 +0100184 : "")
185 )
186 .setStatus(failed ? "Warning" : "Success")
187 ],
188 components: []
189 });
pineafan63fc5e22022-08-04 22:04:10 +0100190};
pineafan32767212022-03-14 21:27:39 +0000191
TheCodedProff86ba092023-01-27 17:10:07 -0500192const check = async (interaction: CommandInteraction, partial: boolean = false) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100193 const member = interaction.member as GuildMember;
TheCodedProff86ba092023-01-27 17:10:07 -0500194 // Check if the user has manage_nicknames permission
195 if (!member.permissions.has("ManageNicknames")) return "You do not have the *Manage Nicknames* permission";
196 if (partial) return true;
PineaFan1dee28f2023-01-16 22:09:07 +0000197 const me = interaction.guild!.members.me!;
Skyler Grey75ea9172022-08-06 10:22:23 +0100198 const apply = interaction.options.getMember("user") as GuildMember;
pineafan62ce1922022-08-25 20:34:45 +0100199 const memberPos = member.roles.cache.size ? member.roles.highest.position : 0;
200 const mePos = me.roles.cache.size ? me.roles.highest.position : 0;
201 const applyPos = apply.roles.cache.size ? apply.roles.highest.position : 0;
PineaFan1dee28f2023-01-16 22:09:07 +0000202 if (!interaction.guild) return false;
pineafanc1c18792022-08-03 21:41:36 +0100203 // Do not allow any changing of the owner
PineaFan0d06edc2023-01-17 22:10:31 +0000204 if (member.id === interaction.guild.ownerId) return "You cannot change the owner's nickname";
pineafan377794f2022-04-18 19:01:01 +0100205 // Check if Nucleus can change the nickname
PineaFan0d06edc2023-01-17 22:10:31 +0000206 if (!(mePos > applyPos)) return "I do not have a role higher than that member";
pineafan377794f2022-04-18 19:01:01 +0100207 // Check if Nucleus has permission to change the nickname
PineaFan0d06edc2023-01-17 22:10:31 +0000208 if (!me.permissions.has("ManageNicknames")) return "I do not have the *Manage Nicknames* permission";
pineafan377794f2022-04-18 19:01:01 +0100209 // Allow the owner to change anyone's nickname
pineafan63fc5e22022-08-04 22:04:10 +0100210 if (member.id === interaction.guild.ownerId) return true;
pineafane625d782022-05-09 18:04:32 +0100211 // Allow changing your own nickname
pineafan63fc5e22022-08-04 22:04:10 +0100212 if (member === apply) return true;
pineafan377794f2022-04-18 19:01:01 +0100213 // Check if the user is below on the role list
PineaFan0d06edc2023-01-17 22:10:31 +0000214 if (!(memberPos > applyPos)) return "You do not have a role higher than that member";
pineafan377794f2022-04-18 19:01:01 +0100215 // Allow change
pineafan63fc5e22022-08-04 22:04:10 +0100216 return true;
217};
pineafan32767212022-03-14 21:27:39 +0000218
Skyler Grey75ea9172022-08-06 10:22:23 +0100219export { command, callback, check };
TheCodedProfa112f612023-01-28 18:06:45 -0500220export const metadata = {
221 longDescription: "Changes the nickname of a member. This is the name that shows in the member list and on messages.",
222 premiumOnly: true,
223}