blob: 989263869166f18228f5d89f2b9790201e324cc5 [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,
11 AutocompleteInteraction
Skyler Grey75ea9172022-08-06 10:22:23 +010012} from "discord.js";
Skyler Greyc634e2b2022-08-06 17:50:48 +010013import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
pineafan41d93562022-07-30 22:10:15 +010014import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
pineafan63fc5e22022-08-04 22:04:10 +010015import client from "../../utils/client.js";
16import confirmationMessage from "../../utils/confirmationMessage.js";
17import generateKeyValueList from "../../utils/generateKeyValueList.js";
Skyler Greyc634e2b2022-08-06 17:50:48 +010018import { ChannelType } from "discord-api-types/v9";
pineafan63fc5e22022-08-04 22:04:10 +010019import getEmojiByName from "../../utils/getEmojiByName.js";
pineafan41d93562022-07-30 22:10:15 +010020
21const command = (builder: SlashCommandSubcommandBuilder) =>
22 builder
pineafan63fc5e22022-08-04 22:04:10 +010023 .setName("welcome")
Skyler Grey11236ba2022-08-08 21:13:33 +010024 .setDescription("Messages and roles sent or given when someone joins the server")
Skyler Grey75ea9172022-08-06 10:22:23 +010025 .addStringOption((option) =>
26 option
27 .setName("message")
Skyler Grey11236ba2022-08-08 21:13:33 +010028 .setDescription("The message to send when someone joins the server")
Skyler Grey75ea9172022-08-06 10:22:23 +010029 .setAutocomplete(true)
30 )
31 .addRoleOption((option) =>
Skyler Grey11236ba2022-08-08 21:13:33 +010032 option.setName("role").setDescription("The role given when someone joins the server")
Skyler Grey75ea9172022-08-06 10:22:23 +010033 )
34 .addRoleOption((option) =>
Skyler Grey11236ba2022-08-08 21:13:33 +010035 option.setName("ping").setDescription("The role pinged when someone joins the server")
Skyler Grey75ea9172022-08-06 10:22:23 +010036 )
37 .addChannelOption((option) =>
38 option
39 .setName("channel")
Skyler Grey11236ba2022-08-08 21:13:33 +010040 .setDescription("The channel the welcome message should be sent to")
PineaFan64486c42022-12-28 09:21:04 +000041 .addChannelTypes(ChannelType.GuildText)
Skyler Grey75ea9172022-08-06 10:22:23 +010042 );
pineafan41d93562022-07-30 22:10:15 +010043
Skyler Greyc634e2b2022-08-06 17:50:48 +010044const callback = async (interaction: CommandInteraction): Promise<unknown> => {
Skyler Grey11236ba2022-08-08 21:13:33 +010045 const { renderRole, renderChannel, log, NucleusColors, entry, renderUser } = client.logger;
Skyler Grey75ea9172022-08-06 10:22:23 +010046 await interaction.reply({
47 embeds: LoadingEmbed,
48 fetchReply: true,
49 ephemeral: true
50 });
Skyler Grey1a67e182022-08-04 23:05:44 +010051 let m: Message;
Skyler Grey75ea9172022-08-06 10:22:23 +010052 if (
53 interaction.options.getRole("role") ||
54 interaction.options.getChannel("channel") ||
55 interaction.options.getString("message")
56 ) {
pineafan4e425942022-08-08 22:01:47 +010057 let role: Role | null;
58 let ping: Role | null;
59 let channel: Channel | null;
60 const message: string | null = interaction.options.getString("message");
pineafan41d93562022-07-30 22:10:15 +010061 try {
pineafan4e425942022-08-08 22:01:47 +010062 role = interaction.options.getRole("role") as Role | null;
63 ping = interaction.options.getRole("ping") as Role | null;
pineafan41d93562022-07-30 22:10:15 +010064 } catch {
Skyler Grey75ea9172022-08-06 10:22:23 +010065 return await interaction.editReply({
66 embeds: [
67 new EmojiEmbed()
68 .setEmoji("GUILD.ROLES.DELETE")
69 .setTitle("Welcome Events")
Skyler Grey11236ba2022-08-08 21:13:33 +010070 .setDescription("The role you provided is not a valid role")
Skyler Grey75ea9172022-08-06 10:22:23 +010071 .setStatus("Danger")
72 ]
73 });
pineafan41d93562022-07-30 22:10:15 +010074 }
pineafan41d93562022-07-30 22:10:15 +010075 try {
pineafan4e425942022-08-08 22:01:47 +010076 channel = interaction.options.getChannel("channel") as Channel | null;
pineafan41d93562022-07-30 22:10:15 +010077 } catch {
Skyler Grey75ea9172022-08-06 10:22:23 +010078 return await interaction.editReply({
79 embeds: [
80 new EmojiEmbed()
81 .setEmoji("GUILD.ROLES.DELETE")
82 .setTitle("Welcome Events")
Skyler Grey11236ba2022-08-08 21:13:33 +010083 .setDescription("The channel you provided is not a valid channel")
Skyler Grey75ea9172022-08-06 10:22:23 +010084 .setStatus("Danger")
85 ]
86 });
pineafan41d93562022-07-30 22:10:15 +010087 }
pineafan4e425942022-08-08 22:01:47 +010088 const options: {
89 role?: string;
90 ping?: string;
91 channel?: string;
92 message?: string;
93 } = {};
94
Skyler Grey75ea9172022-08-06 10:22:23 +010095 if (role) options.role = renderRole(role);
96 if (ping) options.ping = renderRole(ping);
97 if (channel) options.channel = renderChannel(channel);
98 if (message) options.message = "\n> " + message;
pineafan63fc5e22022-08-04 22:04:10 +010099 const confirmation = await new confirmationMessage(interaction)
pineafan62ce1922022-08-25 20:34:45 +0100100 .setEmoji("GUILD.ROLES.EDIT", "GUILD.ROLES.DELETE")
pineafan41d93562022-07-30 22:10:15 +0100101 .setTitle("Welcome Events")
102 .setDescription(generateKeyValueList(options))
103 .setColor("Warning")
104 .setInverted(true)
pineafan63fc5e22022-08-04 22:04:10 +0100105 .send(true);
106 if (confirmation.cancelled) return;
pineafan41d93562022-07-30 22:10:15 +0100107 if (confirmation.success) {
108 try {
pineafan4e425942022-08-08 22:01:47 +0100109 const toChange: {
110 "welcome.role"?: string;
111 "welcome.ping"?: string;
112 "welcome.channel"?: string;
113 "welcome.message"?: string;
114 } = {};
pineafan63fc5e22022-08-04 22:04:10 +0100115 if (role) toChange["welcome.role"] = role.id;
116 if (ping) toChange["welcome.ping"] = ping.id;
117 if (channel) toChange["welcome.channel"] = channel.id;
118 if (message) toChange["welcome.message"] = message;
pineafan4e425942022-08-08 22:01:47 +0100119 await client.database.guilds.write(interaction.guild!.id, toChange);
120 const list: {
121 memberId: ReturnType<typeof entry>;
122 changedBy: ReturnType<typeof entry>;
123 role?: ReturnType<typeof entry>;
124 ping?: ReturnType<typeof entry>;
125 channel?: ReturnType<typeof entry>;
126 message?: ReturnType<typeof entry>;
127 } = {
Skyler Grey11236ba2022-08-08 21:13:33 +0100128 memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
129 changedBy: entry(interaction.user.id, renderUser(interaction.user))
pineafan63fc5e22022-08-04 22:04:10 +0100130 };
Skyler Grey75ea9172022-08-06 10:22:23 +0100131 if (role) list.role = entry(role.id, renderRole(role));
132 if (ping) list.ping = entry(ping.id, renderRole(ping));
Skyler Grey11236ba2022-08-08 21:13:33 +0100133 if (channel) list.channel = entry(channel.id, renderChannel(channel.id));
Skyler Grey75ea9172022-08-06 10:22:23 +0100134 if (message) list.message = entry(message, `\`${message}\``);
pineafan63fc5e22022-08-04 22:04:10 +0100135 const data = {
Skyler Grey75ea9172022-08-06 10:22:23 +0100136 meta: {
pineafan63fc5e22022-08-04 22:04:10 +0100137 type: "welcomeSettingsUpdated",
138 displayName: "Welcome Settings Changed",
139 calculateType: "nucleusSettingsUpdated",
140 color: NucleusColors.green,
141 emoji: "CONTROL.BLOCKTICK",
142 timestamp: new Date().getTime()
143 },
144 list: list,
145 hidden: {
pineafan4e425942022-08-08 22:01:47 +0100146 guild: interaction.guild!.id
pineafan41d93562022-07-30 22:10:15 +0100147 }
pineafan63fc5e22022-08-04 22:04:10 +0100148 };
149 log(data);
pineafan41d93562022-07-30 22:10:15 +0100150 } catch (e) {
pineafan63fc5e22022-08-04 22:04:10 +0100151 console.log(e);
Skyler Grey75ea9172022-08-06 10:22:23 +0100152 return interaction.editReply({
153 embeds: [
154 new EmojiEmbed()
155 .setTitle("Welcome Events")
Skyler Grey11236ba2022-08-08 21:13:33 +0100156 .setDescription("Something went wrong while updating welcome settings")
Skyler Grey75ea9172022-08-06 10:22:23 +0100157 .setStatus("Danger")
158 .setEmoji("GUILD.ROLES.DELETE")
159 ],
160 components: []
161 });
pineafan41d93562022-07-30 22:10:15 +0100162 }
163 } else {
Skyler Grey75ea9172022-08-06 10:22:23 +0100164 return interaction.editReply({
165 embeds: [
166 new EmojiEmbed()
167 .setTitle("Welcome Events")
168 .setDescription("No changes were made")
169 .setStatus("Success")
170 .setEmoji("GUILD.ROLES.CREATE")
171 ],
172 components: []
173 });
pineafan41d93562022-07-30 22:10:15 +0100174 }
175 }
pineafan63fc5e22022-08-04 22:04:10 +0100176 let lastClicked = null;
Skyler Greyad002172022-08-16 18:48:26 +0100177 let timedOut = false;
178 do {
pineafan4e425942022-08-08 22:01:47 +0100179 const config = await client.database.guilds.read(interaction.guild!.id);
Skyler Grey75ea9172022-08-06 10:22:23 +0100180 m = (await interaction.editReply({
181 embeds: [
182 new EmojiEmbed()
183 .setTitle("Welcome Events")
184 .setDescription(
Skyler Grey11236ba2022-08-08 21:13:33 +0100185 `**Message:** ${config.welcome.message ? `\n> ${config.welcome.message}` : "*None set*"}\n` +
Skyler Grey75ea9172022-08-06 10:22:23 +0100186 `**Role:** ${
187 config.welcome.role
pineafan4e425942022-08-08 22:01:47 +0100188 ? renderRole(await interaction.guild!.roles.fetch(config.welcome.role))
Skyler Grey75ea9172022-08-06 10:22:23 +0100189 : "*None set*"
190 }\n` +
191 `**Ping:** ${
192 config.welcome.ping
pineafan4e425942022-08-08 22:01:47 +0100193 ? renderRole(await interaction.guild!.roles.fetch(config.welcome.ping))
Skyler Grey75ea9172022-08-06 10:22:23 +0100194 : "*None set*"
195 }\n` +
196 `**Channel:** ${
197 config.welcome.channel
198 ? config.welcome.channel == "dm"
199 ? "DM"
pineafan4e425942022-08-08 22:01:47 +0100200 : renderChannel(await interaction.guild!.channels.fetch(config.welcome.channel))
Skyler Grey75ea9172022-08-06 10:22:23 +0100201 : "*None set*"
202 }`
203 )
204 .setStatus("Success")
205 .setEmoji("CHANNEL.TEXT.CREATE")
206 ],
207 components: [
PineaFan538d3752023-01-12 21:48:23 +0000208 new ActionRowBuilder<ButtonBuilder>().addComponents([
TheCodedProf21c08592022-09-13 14:14:43 -0400209 new ButtonBuilder()
Skyler Grey11236ba2022-08-08 21:13:33 +0100210 .setLabel(lastClicked == "clear-message" ? "Click again to confirm" : "Clear Message")
Skyler Grey75ea9172022-08-06 10:22:23 +0100211 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
212 .setCustomId("clear-message")
213 .setDisabled(!config.welcome.message)
TheCodedProf21c08592022-09-13 14:14:43 -0400214 .setStyle(ButtonStyle.Danger),
215 new ButtonBuilder()
Skyler Grey11236ba2022-08-08 21:13:33 +0100216 .setLabel(lastClicked == "clear-role" ? "Click again to confirm" : "Clear Role")
Skyler Grey75ea9172022-08-06 10:22:23 +0100217 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
218 .setCustomId("clear-role")
219 .setDisabled(!config.welcome.role)
TheCodedProf21c08592022-09-13 14:14:43 -0400220 .setStyle(ButtonStyle.Danger),
221 new ButtonBuilder()
Skyler Grey11236ba2022-08-08 21:13:33 +0100222 .setLabel(lastClicked == "clear-ping" ? "Click again to confirm" : "Clear Ping")
Skyler Grey75ea9172022-08-06 10:22:23 +0100223 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
224 .setCustomId("clear-ping")
225 .setDisabled(!config.welcome.ping)
TheCodedProf21c08592022-09-13 14:14:43 -0400226 .setStyle(ButtonStyle.Danger),
227 new ButtonBuilder()
Skyler Grey11236ba2022-08-08 21:13:33 +0100228 .setLabel(lastClicked == "clear-channel" ? "Click again to confirm" : "Clear Channel")
Skyler Grey75ea9172022-08-06 10:22:23 +0100229 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
230 .setCustomId("clear-channel")
231 .setDisabled(!config.welcome.channel)
TheCodedProf21c08592022-09-13 14:14:43 -0400232 .setStyle(ButtonStyle.Danger),
233 new ButtonBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100234 .setLabel("Set Channel to DM")
235 .setCustomId("set-channel-dm")
236 .setDisabled(config.welcome.channel == "dm")
TheCodedProf21c08592022-09-13 14:14:43 -0400237 .setStyle(ButtonStyle.Secondary)
Skyler Grey75ea9172022-08-06 10:22:23 +0100238 ])
239 ]
240 })) as Message;
Skyler Grey1a67e182022-08-04 23:05:44 +0100241 let i: MessageComponentInteraction;
pineafan41d93562022-07-30 22:10:15 +0100242 try {
PineaFan0d06edc2023-01-17 22:10:31 +0000243 i = await m.awaitMessageComponent({
244 time: 300000,
245 filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
246 });
pineafan41d93562022-07-30 22:10:15 +0100247 } catch (e) {
Skyler Greyad002172022-08-16 18:48:26 +0100248 timedOut = true;
249 continue;
pineafan41d93562022-07-30 22:10:15 +0100250 }
pineafan63fc5e22022-08-04 22:04:10 +0100251 i.deferUpdate();
pineafan41d93562022-07-30 22:10:15 +0100252 if (i.customId == "clear-message") {
253 if (lastClicked == "clear-message") {
pineafan4e425942022-08-08 22:01:47 +0100254 await client.database.guilds.write(interaction.guild!.id, {
Skyler Grey75ea9172022-08-06 10:22:23 +0100255 "welcome.message": null
256 });
pineafan63fc5e22022-08-04 22:04:10 +0100257 lastClicked = null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100258 } else {
259 lastClicked = "clear-message";
260 }
pineafan41d93562022-07-30 22:10:15 +0100261 } else if (i.customId == "clear-role") {
262 if (lastClicked == "clear-role") {
pineafan4e425942022-08-08 22:01:47 +0100263 await client.database.guilds.write(interaction.guild!.id, {
Skyler Grey75ea9172022-08-06 10:22:23 +0100264 "welcome.role": null
265 });
pineafan63fc5e22022-08-04 22:04:10 +0100266 lastClicked = null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100267 } else {
268 lastClicked = "clear-role";
269 }
pineafan41d93562022-07-30 22:10:15 +0100270 } else if (i.customId == "clear-ping") {
271 if (lastClicked == "clear-ping") {
pineafan4e425942022-08-08 22:01:47 +0100272 await client.database.guilds.write(interaction.guild!.id, {
Skyler Grey75ea9172022-08-06 10:22:23 +0100273 "welcome.ping": null
274 });
pineafan63fc5e22022-08-04 22:04:10 +0100275 lastClicked = null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100276 } else {
277 lastClicked = "clear-ping";
278 }
pineafan41d93562022-07-30 22:10:15 +0100279 } else if (i.customId == "clear-channel") {
280 if (lastClicked == "clear-channel") {
pineafan4e425942022-08-08 22:01:47 +0100281 await client.database.guilds.write(interaction.guild!.id, {
Skyler Grey75ea9172022-08-06 10:22:23 +0100282 "welcome.channel": null
283 });
pineafan63fc5e22022-08-04 22:04:10 +0100284 lastClicked = null;
Skyler Grey75ea9172022-08-06 10:22:23 +0100285 } else {
286 lastClicked = "clear-channel";
287 }
pineafan41d93562022-07-30 22:10:15 +0100288 } else if (i.customId == "set-channel-dm") {
pineafan4e425942022-08-08 22:01:47 +0100289 await client.database.guilds.write(interaction.guild!.id, {
Skyler Grey75ea9172022-08-06 10:22:23 +0100290 "welcome.channel": "dm"
291 });
pineafan63fc5e22022-08-04 22:04:10 +0100292 lastClicked = null;
pineafan41d93562022-07-30 22:10:15 +0100293 }
Skyler Greyad002172022-08-16 18:48:26 +0100294 } while (!timedOut);
Skyler Grey75ea9172022-08-06 10:22:23 +0100295 await interaction.editReply({
Skyler Greyad002172022-08-16 18:48:26 +0100296 embeds: [m.embeds[0]!.setFooter({ text: "Message timed out" })],
Skyler Grey75ea9172022-08-06 10:22:23 +0100297 components: []
298 });
pineafan63fc5e22022-08-04 22:04:10 +0100299};
pineafan41d93562022-07-30 22:10:15 +0100300
Skyler Grey1a67e182022-08-04 23:05:44 +0100301const check = (interaction: CommandInteraction) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100302 const member = interaction.member as Discord.GuildMember;
PineaFan538d3752023-01-12 21:48:23 +0000303 if (!member.permissions.has("ManageGuild"))
PineaFan0d06edc2023-01-17 22:10:31 +0000304 return "You must have the *Manage Server* permission to use this command";
pineafan41d93562022-07-30 22:10:15 +0100305 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100306};
pineafan41d93562022-07-30 22:10:15 +0100307
PineaFan538d3752023-01-12 21:48:23 +0000308const autocomplete = async (interaction: AutocompleteInteraction): Promise<string[]> => {
309 const validReplacements = ["serverName", "memberCount", "memberCount:bots", "memberCount:humans"]
310 if (!interaction.guild) return [];
311 const prompt = interaction.options.getString("message");
312 const autocompletions = [];
313 if ( prompt === null ) {
314 for (const replacement of validReplacements) {
315 autocompletions.push(`{${replacement}}`);
316 };
317 return autocompletions;
318 };
319 const beforeLastOpenBracket = prompt.match(/(.*){[^{}]{0,15}$/);
320 const afterLastOpenBracket = prompt.match(/{[^{}]{0,15}$/);
321 if (beforeLastOpenBracket !== null) {
322 if (afterLastOpenBracket !== null) {
323 for (const replacement of validReplacements) {
324 if (replacement.startsWith(afterLastOpenBracket[0].slice(1))) {
325 autocompletions.push(`${beforeLastOpenBracket[1]}{${replacement}}`);
326 }
327 }
328 } else {
329 for (const replacement of validReplacements) {
330 autocompletions.push(`${beforeLastOpenBracket[1]}{${replacement}}`);
331 }
332 }
333 } else {
334 for (const replacement of validReplacements) {
335 autocompletions.push(`${prompt} {${replacement}}`);
336 }
337 }
338 return autocompletions;
339};
340
341export { command, callback, check, autocomplete };