blob: fcd0f7665760e94c1408cbbef2f80eca6c791cd9 [file] [log] [blame]
PineaFan0d06edc2023-01-17 22:10:31 +00001import { LoadingEmbed } from "../../utils/defaults.js";
Skyler Grey75ea9172022-08-06 10:22:23 +01002import Discord, {
3 Channel,
4 CommandInteraction,
5 Message,
TheCodedProf21c08592022-09-13 14:14:43 -04006 ActionRowBuilder,
7 ButtonBuilder,
Skyler Grey75ea9172022-08-06 10:22:23 +01008 MessageComponentInteraction,
TheCodedProf21c08592022-09-13 14:14:43 -04009 Role,
PineaFan538d3752023-01-12 21:48:23 +000010 ButtonStyle,
TheCodedProf51296102023-01-18 22:35:02 -050011 AutocompleteInteraction,
12 GuildChannel,
13 EmbedBuilder
Skyler Grey75ea9172022-08-06 10:22:23 +010014} from "discord.js";
Skyler Greyc634e2b2022-08-06 17:50:48 +010015import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
pineafan41d93562022-07-30 22:10:15 +010016import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
pineafan63fc5e22022-08-04 22:04:10 +010017import client from "../../utils/client.js";
18import confirmationMessage from "../../utils/confirmationMessage.js";
19import generateKeyValueList from "../../utils/generateKeyValueList.js";
Skyler Greyc634e2b2022-08-06 17:50:48 +010020import { ChannelType } from "discord-api-types/v9";
pineafan63fc5e22022-08-04 22:04:10 +010021import getEmojiByName from "../../utils/getEmojiByName.js";
pineafan41d93562022-07-30 22:10:15 +010022
23const command = (builder: SlashCommandSubcommandBuilder) =>
24 builder
pineafan63fc5e22022-08-04 22:04:10 +010025 .setName("welcome")
Skyler Grey11236ba2022-08-08 21:13:33 +010026 .setDescription("Messages and roles sent or given when someone joins the server")
Skyler Grey75ea9172022-08-06 10:22:23 +010027 .addStringOption((option) =>
28 option
29 .setName("message")
Skyler Grey11236ba2022-08-08 21:13:33 +010030 .setDescription("The message to send when someone joins the server")
Skyler Grey75ea9172022-08-06 10:22:23 +010031 .setAutocomplete(true)
32 )
33 .addRoleOption((option) =>
Skyler Grey11236ba2022-08-08 21:13:33 +010034 option.setName("role").setDescription("The role given when someone joins the server")
Skyler Grey75ea9172022-08-06 10:22:23 +010035 )
36 .addRoleOption((option) =>
Skyler Grey11236ba2022-08-08 21:13:33 +010037 option.setName("ping").setDescription("The role pinged when someone joins the server")
Skyler Grey75ea9172022-08-06 10:22:23 +010038 )
39 .addChannelOption((option) =>
40 option
41 .setName("channel")
Skyler Grey11236ba2022-08-08 21:13:33 +010042 .setDescription("The channel the welcome message should be sent to")
PineaFan64486c42022-12-28 09:21:04 +000043 .addChannelTypes(ChannelType.GuildText)
Skyler Grey75ea9172022-08-06 10:22:23 +010044 );
pineafan41d93562022-07-30 22:10:15 +010045
Skyler Greyc634e2b2022-08-06 17:50:48 +010046const callback = async (interaction: CommandInteraction): Promise<unknown> => {
Skyler Grey11236ba2022-08-08 21:13:33 +010047 const { renderRole, renderChannel, log, NucleusColors, entry, renderUser } = client.logger;
Skyler Grey75ea9172022-08-06 10:22:23 +010048 await interaction.reply({
49 embeds: LoadingEmbed,
50 fetchReply: true,
51 ephemeral: true
52 });
Skyler Grey1a67e182022-08-04 23:05:44 +010053 let m: Message;
Skyler Grey75ea9172022-08-06 10:22:23 +010054 if (
TheCodedProf51296102023-01-18 22:35:02 -050055 interaction.options.get("role")?.role ||
56 interaction.options.get("channel")?.channel ||
57 interaction.options.get("message")?.value as string
Skyler Grey75ea9172022-08-06 10:22:23 +010058 ) {
pineafan4e425942022-08-08 22:01:47 +010059 let role: Role | null;
60 let ping: Role | null;
61 let channel: Channel | null;
TheCodedProf51296102023-01-18 22:35:02 -050062 const message: string | null = interaction.options.get("message")?.value as string | null;
pineafan41d93562022-07-30 22:10:15 +010063 try {
TheCodedProf51296102023-01-18 22:35:02 -050064 role = interaction.options.get("role")?.role as Role | null;
65 ping = interaction.options.get("ping")?.role as Role | null;
pineafan41d93562022-07-30 22:10:15 +010066 } catch {
Skyler Grey75ea9172022-08-06 10:22:23 +010067 return await interaction.editReply({
68 embeds: [
69 new EmojiEmbed()
70 .setEmoji("GUILD.ROLES.DELETE")
71 .setTitle("Welcome Events")
Skyler Grey11236ba2022-08-08 21:13:33 +010072 .setDescription("The role you provided is not a valid role")
Skyler Grey75ea9172022-08-06 10:22:23 +010073 .setStatus("Danger")
74 ]
75 });
pineafan41d93562022-07-30 22:10:15 +010076 }
pineafan41d93562022-07-30 22:10:15 +010077 try {
TheCodedProf51296102023-01-18 22:35:02 -050078 channel = interaction.options.get("channel")?.channel as Channel | null;
pineafan41d93562022-07-30 22:10:15 +010079 } catch {
Skyler Grey75ea9172022-08-06 10:22:23 +010080 return await interaction.editReply({
81 embeds: [
82 new EmojiEmbed()
83 .setEmoji("GUILD.ROLES.DELETE")
84 .setTitle("Welcome Events")
Skyler Grey11236ba2022-08-08 21:13:33 +010085 .setDescription("The channel you provided is not a valid channel")
Skyler Grey75ea9172022-08-06 10:22:23 +010086 .setStatus("Danger")
87 ]
88 });
pineafan41d93562022-07-30 22:10:15 +010089 }
pineafan4e425942022-08-08 22:01:47 +010090 const options: {
91 role?: string;
92 ping?: string;
93 channel?: string;
94 message?: string;
95 } = {};
96
Skyler Grey75ea9172022-08-06 10:22:23 +010097 if (role) options.role = renderRole(role);
98 if (ping) options.ping = renderRole(ping);
TheCodedProf51296102023-01-18 22:35:02 -050099 if (channel) options.channel = renderChannel(channel as GuildChannel);
Skyler Grey75ea9172022-08-06 10:22:23 +0100100 if (message) options.message = "\n> " + message;
pineafan63fc5e22022-08-04 22:04:10 +0100101 const confirmation = await new confirmationMessage(interaction)
TheCodedProf51296102023-01-18 22:35:02 -0500102 .setEmoji("GUILD.ROLES.EDIT")
pineafan41d93562022-07-30 22:10:15 +0100103 .setTitle("Welcome Events")
104 .setDescription(generateKeyValueList(options))
105 .setColor("Warning")
PineaFan5d98a4b2023-01-19 16:15:47 +0000106 .setFailedMessage("No changes were made", "Success", "GUILD.ROLES.CREATE")
pineafan41d93562022-07-30 22:10:15 +0100107 .setInverted(true)
pineafan63fc5e22022-08-04 22:04:10 +0100108 .send(true);
109 if (confirmation.cancelled) return;
pineafan41d93562022-07-30 22:10:15 +0100110 if (confirmation.success) {
111 try {
pineafan4e425942022-08-08 22:01:47 +0100112 const toChange: {
113 "welcome.role"?: string;
114 "welcome.ping"?: string;
115 "welcome.channel"?: string;
116 "welcome.message"?: string;
117 } = {};
pineafan63fc5e22022-08-04 22:04:10 +0100118 if (role) toChange["welcome.role"] = role.id;
119 if (ping) toChange["welcome.ping"] = ping.id;
120 if (channel) toChange["welcome.channel"] = channel.id;
121 if (message) toChange["welcome.message"] = message;
pineafan4e425942022-08-08 22:01:47 +0100122 await client.database.guilds.write(interaction.guild!.id, toChange);
123 const list: {
124 memberId: ReturnType<typeof entry>;
125 changedBy: ReturnType<typeof entry>;
126 role?: ReturnType<typeof entry>;
127 ping?: ReturnType<typeof entry>;
128 channel?: ReturnType<typeof entry>;
129 message?: ReturnType<typeof entry>;
130 } = {
Skyler Grey11236ba2022-08-08 21:13:33 +0100131 memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
132 changedBy: entry(interaction.user.id, renderUser(interaction.user))
pineafan63fc5e22022-08-04 22:04:10 +0100133 };
Skyler Grey75ea9172022-08-06 10:22:23 +0100134 if (role) list.role = entry(role.id, renderRole(role));
135 if (ping) list.ping = entry(ping.id, renderRole(ping));
TheCodedProf51296102023-01-18 22:35:02 -0500136 if (channel) list.channel = entry(channel.id, renderChannel(channel as GuildChannel));
Skyler Grey75ea9172022-08-06 10:22:23 +0100137 if (message) list.message = entry(message, `\`${message}\``);
pineafan63fc5e22022-08-04 22:04:10 +0100138 const data = {
Skyler Grey75ea9172022-08-06 10:22:23 +0100139 meta: {
pineafan63fc5e22022-08-04 22:04:10 +0100140 type: "welcomeSettingsUpdated",
141 displayName: "Welcome Settings Changed",
142 calculateType: "nucleusSettingsUpdated",
143 color: NucleusColors.green,
144 emoji: "CONTROL.BLOCKTICK",
145 timestamp: new Date().getTime()
146 },
147 list: list,
148 hidden: {
pineafan4e425942022-08-08 22:01:47 +0100149 guild: interaction.guild!.id
pineafan41d93562022-07-30 22:10:15 +0100150 }
pineafan63fc5e22022-08-04 22:04:10 +0100151 };
152 log(data);
pineafan41d93562022-07-30 22:10:15 +0100153 } catch (e) {
pineafan63fc5e22022-08-04 22:04:10 +0100154 console.log(e);
Skyler Grey75ea9172022-08-06 10:22:23 +0100155 return interaction.editReply({
156 embeds: [
157 new EmojiEmbed()
158 .setTitle("Welcome Events")
Skyler Grey11236ba2022-08-08 21:13:33 +0100159 .setDescription("Something went wrong while updating welcome settings")
Skyler Grey75ea9172022-08-06 10:22:23 +0100160 .setStatus("Danger")
161 .setEmoji("GUILD.ROLES.DELETE")
162 ],
163 components: []
164 });
pineafan41d93562022-07-30 22:10:15 +0100165 }
166 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +0100167 return interaction.editReply({
168 embeds: [
169 new EmojiEmbed()
170 .setTitle("Welcome Events")
171 .setDescription("No changes were made")
172 .setStatus("Success")
173 .setEmoji("GUILD.ROLES.CREATE")
174 ],
175 components: []
176 });
pineafan41d93562022-07-30 22:10:15 +0100177 }
178 }
pineafan63fc5e22022-08-04 22:04:10 +0100179 let lastClicked = null;
Skyler Greyad002172022-08-16 18:48:26 +0100180 let timedOut = false;
181 do {
pineafan4e425942022-08-08 22:01:47 +0100182 const config = await client.database.guilds.read(interaction.guild!.id);
Skyler Grey75ea9172022-08-06 10:22:23 +0100183 m = (await interaction.editReply({
184 embeds: [
185 new EmojiEmbed()
186 .setTitle("Welcome Events")
187 .setDescription(
Skyler Grey11236ba2022-08-08 21:13:33 +0100188 `**Message:** ${config.welcome.message ? `\n> ${config.welcome.message}` : "*None set*"}\n` +
Skyler Grey75ea9172022-08-06 10:22:23 +0100189 `**Role:** ${
190 config.welcome.role
TheCodedProf51296102023-01-18 22:35:02 -0500191 ? renderRole((await interaction.guild!.roles.fetch(config.welcome.role))!)
Skyler Grey75ea9172022-08-06 10:22:23 +0100192 : "*None set*"
193 }\n` +
194 `**Ping:** ${
195 config.welcome.ping
TheCodedProf51296102023-01-18 22:35:02 -0500196 ? renderRole((await interaction.guild!.roles.fetch(config.welcome.ping))!)
Skyler Grey75ea9172022-08-06 10:22:23 +0100197 : "*None set*"
198 }\n` +
199 `**Channel:** ${
200 config.welcome.channel
201 ? config.welcome.channel == "dm"
202 ? "DM"
TheCodedProf51296102023-01-18 22:35:02 -0500203 : renderChannel((await interaction.guild!.channels.fetch(config.welcome.channel))!)
Skyler Grey75ea9172022-08-06 10:22:23 +0100204 : "*None set*"
205 }`
206 )
207 .setStatus("Success")
208 .setEmoji("CHANNEL.TEXT.CREATE")
209 ],
210 components: [
PineaFan538d3752023-01-12 21:48:23 +0000211 new ActionRowBuilder<ButtonBuilder>().addComponents([
TheCodedProf21c08592022-09-13 14:14:43 -0400212 new ButtonBuilder()
Skyler Grey11236ba2022-08-08 21:13:33 +0100213 .setLabel(lastClicked == "clear-message" ? "Click again to confirm" : "Clear Message")
Skyler Grey75ea9172022-08-06 10:22:23 +0100214 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
215 .setCustomId("clear-message")
216 .setDisabled(!config.welcome.message)
TheCodedProf21c08592022-09-13 14:14:43 -0400217 .setStyle(ButtonStyle.Danger),
218 new ButtonBuilder()
Skyler Grey11236ba2022-08-08 21:13:33 +0100219 .setLabel(lastClicked == "clear-role" ? "Click again to confirm" : "Clear Role")
Skyler Grey75ea9172022-08-06 10:22:23 +0100220 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
221 .setCustomId("clear-role")
222 .setDisabled(!config.welcome.role)
TheCodedProf21c08592022-09-13 14:14:43 -0400223 .setStyle(ButtonStyle.Danger),
224 new ButtonBuilder()
Skyler Grey11236ba2022-08-08 21:13:33 +0100225 .setLabel(lastClicked == "clear-ping" ? "Click again to confirm" : "Clear Ping")
Skyler Grey75ea9172022-08-06 10:22:23 +0100226 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
227 .setCustomId("clear-ping")
228 .setDisabled(!config.welcome.ping)
TheCodedProf21c08592022-09-13 14:14:43 -0400229 .setStyle(ButtonStyle.Danger),
230 new ButtonBuilder()
Skyler Grey11236ba2022-08-08 21:13:33 +0100231 .setLabel(lastClicked == "clear-channel" ? "Click again to confirm" : "Clear Channel")
Skyler Grey75ea9172022-08-06 10:22:23 +0100232 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
233 .setCustomId("clear-channel")
234 .setDisabled(!config.welcome.channel)
TheCodedProf21c08592022-09-13 14:14:43 -0400235 .setStyle(ButtonStyle.Danger),
236 new ButtonBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100237 .setLabel("Set Channel to DM")
238 .setCustomId("set-channel-dm")
239 .setDisabled(config.welcome.channel == "dm")
TheCodedProf21c08592022-09-13 14:14:43 -0400240 .setStyle(ButtonStyle.Secondary)
Skyler Grey75ea9172022-08-06 10:22:23 +0100241 ])
242 ]
243 })) as Message;
Skyler Grey1a67e182022-08-04 23:05:44 +0100244 let i: MessageComponentInteraction;
pineafan41d93562022-07-30 22:10:15 +0100245 try {
PineaFan0d06edc2023-01-17 22:10:31 +0000246 i = await m.awaitMessageComponent({
247 time: 300000,
TheCodedProf267563a2023-01-21 17:00:57 -0500248 filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id && i.message.id === m.id }
PineaFan0d06edc2023-01-17 22:10:31 +0000249 });
pineafan41d93562022-07-30 22:10:15 +0100250 } catch (e) {
Skyler Greyad002172022-08-16 18:48:26 +0100251 timedOut = true;
252 continue;
pineafan41d93562022-07-30 22:10:15 +0100253 }
TheCodedProf267563a2023-01-21 17:00:57 -0500254 await i.deferUpdate();
pineafan41d93562022-07-30 22:10:15 +0100255 if (i.customId == "clear-message") {
256 if (lastClicked == "clear-message") {
pineafan4e425942022-08-08 22:01:47 +0100257 await client.database.guilds.write(interaction.guild!.id, {
Skyler Grey75ea9172022-08-06 10:22:23 +0100258 "welcome.message": null
259 });
pineafan63fc5e22022-08-04 22:04:10 +0100260 lastClicked = null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100261 } else {
262 lastClicked = "clear-message";
263 }
pineafan41d93562022-07-30 22:10:15 +0100264 } else if (i.customId == "clear-role") {
265 if (lastClicked == "clear-role") {
pineafan4e425942022-08-08 22:01:47 +0100266 await client.database.guilds.write(interaction.guild!.id, {
Skyler Grey75ea9172022-08-06 10:22:23 +0100267 "welcome.role": null
268 });
pineafan63fc5e22022-08-04 22:04:10 +0100269 lastClicked = null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100270 } else {
271 lastClicked = "clear-role";
272 }
pineafan41d93562022-07-30 22:10:15 +0100273 } else if (i.customId == "clear-ping") {
274 if (lastClicked == "clear-ping") {
pineafan4e425942022-08-08 22:01:47 +0100275 await client.database.guilds.write(interaction.guild!.id, {
Skyler Grey75ea9172022-08-06 10:22:23 +0100276 "welcome.ping": null
277 });
pineafan63fc5e22022-08-04 22:04:10 +0100278 lastClicked = null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100279 } else {
280 lastClicked = "clear-ping";
281 }
pineafan41d93562022-07-30 22:10:15 +0100282 } else if (i.customId == "clear-channel") {
283 if (lastClicked == "clear-channel") {
pineafan4e425942022-08-08 22:01:47 +0100284 await client.database.guilds.write(interaction.guild!.id, {
Skyler Grey75ea9172022-08-06 10:22:23 +0100285 "welcome.channel": null
286 });
pineafan63fc5e22022-08-04 22:04:10 +0100287 lastClicked = null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100288 } else {
289 lastClicked = "clear-channel";
290 }
pineafan41d93562022-07-30 22:10:15 +0100291 } else if (i.customId == "set-channel-dm") {
pineafan4e425942022-08-08 22:01:47 +0100292 await client.database.guilds.write(interaction.guild!.id, {
Skyler Grey75ea9172022-08-06 10:22:23 +0100293 "welcome.channel": "dm"
294 });
pineafan63fc5e22022-08-04 22:04:10 +0100295 lastClicked = null;
pineafan41d93562022-07-30 22:10:15 +0100296 }
Skyler Greyad002172022-08-16 18:48:26 +0100297 } while (!timedOut);
Skyler Grey75ea9172022-08-06 10:22:23 +0100298 await interaction.editReply({
TheCodedProf51296102023-01-18 22:35:02 -0500299 embeds: [new EmbedBuilder(m.embeds[0]!.data).setFooter({ text: "Message timed out" })],
Skyler Grey75ea9172022-08-06 10:22:23 +0100300 components: []
301 });
pineafan63fc5e22022-08-04 22:04:10 +0100302};
pineafan41d93562022-07-30 22:10:15 +0100303
Skyler Grey1a67e182022-08-04 23:05:44 +0100304const check = (interaction: CommandInteraction) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100305 const member = interaction.member as Discord.GuildMember;
PineaFan538d3752023-01-12 21:48:23 +0000306 if (!member.permissions.has("ManageGuild"))
PineaFan0d06edc2023-01-17 22:10:31 +0000307 return "You must have the *Manage Server* permission to use this command";
pineafan41d93562022-07-30 22:10:15 +0100308 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100309};
pineafan41d93562022-07-30 22:10:15 +0100310
PineaFan538d3752023-01-12 21:48:23 +0000311const autocomplete = async (interaction: AutocompleteInteraction): Promise<string[]> => {
TheCodedProf4a6d5712023-01-19 15:54:40 -0500312 const validReplacements = ["serverName", "memberCount:all", "memberCount:bots", "memberCount:humans"]
PineaFan538d3752023-01-12 21:48:23 +0000313 if (!interaction.guild) return [];
314 const prompt = interaction.options.getString("message");
315 const autocompletions = [];
316 if ( prompt === null ) {
317 for (const replacement of validReplacements) {
318 autocompletions.push(`{${replacement}}`);
319 };
320 return autocompletions;
321 };
322 const beforeLastOpenBracket = prompt.match(/(.*){[^{}]{0,15}$/);
323 const afterLastOpenBracket = prompt.match(/{[^{}]{0,15}$/);
324 if (beforeLastOpenBracket !== null) {
325 if (afterLastOpenBracket !== null) {
326 for (const replacement of validReplacements) {
PineaFan5d98a4b2023-01-19 16:15:47 +0000327 if (replacement.startsWith(afterLastOpenBracket[0]!.slice(1))) {
PineaFan538d3752023-01-12 21:48:23 +0000328 autocompletions.push(`${beforeLastOpenBracket[1]}{${replacement}}`);
329 }
330 }
331 } else {
332 for (const replacement of validReplacements) {
333 autocompletions.push(`${beforeLastOpenBracket[1]}{${replacement}}`);
334 }
335 }
336 } else {
337 for (const replacement of validReplacements) {
338 autocompletions.push(`${prompt} {${replacement}}`);
339 }
340 }
341 return autocompletions;
342};
343
344export { command, callback, check, autocomplete };