blob: bf52dd1a1cb9f56af82bce36970fee9fdc9d0508 [file] [log] [blame]
PineaFan0d06edc2023-01-17 22:10:31 +00001import { LoadingEmbed } from "../../utils/defaults.js";
Skyler Grey75ea9172022-08-06 10:22:23 +01002import Discord, {
Skyler Grey75ea9172022-08-06 10:22:23 +01003 CommandInteraction,
TheCodedProf01cba762023-02-18 15:55:05 -05004 AutocompleteInteraction,
TheCodedProf21c08592022-09-13 14:14:43 -04005 ActionRowBuilder,
6 ButtonBuilder,
PineaFan538d3752023-01-12 21:48:23 +00007 ButtonStyle,
TheCodedProf01cba762023-02-18 15:55:05 -05008 APIMessageComponentEmoji,
9 ChannelSelectMenuBuilder,
10 RoleSelectMenuBuilder,
11 RoleSelectMenuInteraction,
12 ChannelSelectMenuInteraction,
13 ButtonInteraction,
14 ModalBuilder,
15 TextInputBuilder,
16 TextInputStyle,
Skyler Greyda16adf2023-03-05 10:22:12 +000017 ModalSubmitInteraction
Skyler Grey75ea9172022-08-06 10:22:23 +010018} from "discord.js";
TheCodedProff86ba092023-01-27 17:10:07 -050019import type { SlashCommandSubcommandBuilder } from "discord.js";
pineafan41d93562022-07-30 22:10:15 +010020import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
pineafan63fc5e22022-08-04 22:04:10 +010021import client from "../../utils/client.js";
pineafan63fc5e22022-08-04 22:04:10 +010022import getEmojiByName from "../../utils/getEmojiByName.js";
TheCodedProf01cba762023-02-18 15:55:05 -050023import convertCurlyBracketString from "../../utils/convertCurlyBracketString.js";
24import { modalInteractionCollector } from "../../utils/dualCollector.js";
pineafan41d93562022-07-30 22:10:15 +010025
26const command = (builder: SlashCommandSubcommandBuilder) =>
Skyler Greyda16adf2023-03-05 10:22:12 +000027 builder.setName("welcome").setDescription("Messages and roles sent or given when someone joins the server");
pineafan41d93562022-07-30 22:10:15 +010028
TheCodedProf01cba762023-02-18 15:55:05 -050029const callback = async (interaction: CommandInteraction): Promise<void> => {
30 const { renderChannel } = client.logger;
31 const m = await interaction.reply({
Skyler Grey75ea9172022-08-06 10:22:23 +010032 embeds: LoadingEmbed,
33 fetchReply: true,
34 ephemeral: true
35 });
TheCodedProf01cba762023-02-18 15:55:05 -050036 let closed = false;
37 let config = await client.database.guilds.read(interaction.guild!.id);
38 let data = Object.assign({}, config.welcome);
Skyler Greyad002172022-08-16 18:48:26 +010039 do {
Skyler Greyda16adf2023-03-05 10:22:12 +000040 const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
41 new ButtonBuilder()
42 .setCustomId("switch")
43 .setLabel(data.enabled ? "Enabled" : "Disabled")
44 .setStyle(data.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
45 .setEmoji(
46 getEmojiByName(data.enabled ? "CONTROL.TICK" : "CONTROL.CROSS", "id") as APIMessageComponentEmoji
47 ),
48 new ButtonBuilder()
49 .setCustomId("message")
50 .setLabel((data.message ? "Change" : "Set") + "Message")
51 .setStyle(ButtonStyle.Primary)
52 .setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
53 new ButtonBuilder()
54 .setCustomId("channelDM")
55 .setLabel("Send in DMs")
56 .setStyle(ButtonStyle.Primary)
57 .setDisabled(data.channel === "dm"),
58 new ButtonBuilder()
59 .setCustomId("role")
60 .setLabel("Clear Role")
61 .setStyle(ButtonStyle.Danger)
62 .setEmoji(getEmojiByName("CONTROL.CROSS", "id") as APIMessageComponentEmoji),
63 new ButtonBuilder()
64 .setCustomId("save")
65 .setLabel("Save")
66 .setStyle(ButtonStyle.Success)
67 .setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji)
68 .setDisabled(
69 data.enabled === config.welcome.enabled &&
TheCodedProf01cba762023-02-18 15:55:05 -050070 data.message === config.welcome.message &&
71 data.role === config.welcome.role &&
72 data.ping === config.welcome.ping &&
73 data.channel === config.welcome.channel
Skyler Greyda16adf2023-03-05 10:22:12 +000074 )
75 );
TheCodedProf01cba762023-02-18 15:55:05 -050076
Skyler Greyda16adf2023-03-05 10:22:12 +000077 const channelMenu = new ActionRowBuilder<ChannelSelectMenuBuilder>().addComponents(
78 new ChannelSelectMenuBuilder()
79 .setCustomId("channel")
80 .setPlaceholder("Select a channel to send welcome messages to")
81 );
82 const roleMenu = new ActionRowBuilder<RoleSelectMenuBuilder>().addComponents(
83 new RoleSelectMenuBuilder()
84 .setCustomId("roleToGive")
85 .setPlaceholder("Select a role to give to the member when they join the server")
86 );
87 const pingMenu = new ActionRowBuilder<RoleSelectMenuBuilder>().addComponents(
88 new RoleSelectMenuBuilder()
89 .setCustomId("roleToPing")
90 .setPlaceholder("Select a role to ping when a member joins the server")
91 );
TheCodedProf01cba762023-02-18 15:55:05 -050092
93 const embed = new EmojiEmbed()
94 .setTitle("Welcome Settings")
95 .setStatus("Success")
96 .setDescription(
Skyler Greyda16adf2023-03-05 10:22:12 +000097 `${getEmojiByName(data.enabled ? "CONTROL.TICK" : "CONTROL.CROSS")} Welcome messages and roles are ${
98 data.enabled ? "enabled" : "disabled"
99 }\n` +
100 `**Welcome message:** ${
101 data.message
102 ? `\n> ` +
103 (await convertCurlyBracketString(
104 data.message,
105 interaction.user.id,
106 interaction.user.username,
107 interaction.guild!.name,
108 interaction.guild!.members
109 ))
110 : "*None*"
111 }\n` +
112 `**Send message in:** ` +
113 (data.channel ? (data.channel == "dm" ? "DMs" : renderChannel(data.channel)) : `*None set*`) +
114 `\n` +
115 `**Role to ping:** ` +
116 (data.ping ? `<@&${data.ping}>` : `*None set*`) +
117 `\n` +
118 `**Role given on join:** ` +
119 (data.role ? `<@&${data.role}>` : `*None set*`)
120 );
TheCodedProf01cba762023-02-18 15:55:05 -0500121
122 await interaction.editReply({
123 embeds: [embed],
124 components: [buttons, channelMenu, roleMenu, pingMenu]
125 });
126
127 let i: RoleSelectMenuInteraction | ChannelSelectMenuInteraction | ButtonInteraction;
pineafan41d93562022-07-30 22:10:15 +0100128 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000129 i = (await m.awaitMessageComponent({
TheCodedProf01cba762023-02-18 15:55:05 -0500130 filter: (interaction) => interaction.user.id === interaction.user.id,
131 time: 300000
Skyler Greyda16adf2023-03-05 10:22:12 +0000132 })) as RoleSelectMenuInteraction | ChannelSelectMenuInteraction | ButtonInteraction;
pineafan41d93562022-07-30 22:10:15 +0100133 } catch (e) {
TheCodedProf01cba762023-02-18 15:55:05 -0500134 closed = true;
Skyler Greyad002172022-08-16 18:48:26 +0100135 continue;
pineafan41d93562022-07-30 22:10:15 +0100136 }
TheCodedProf01cba762023-02-18 15:55:05 -0500137
Skyler Greyda16adf2023-03-05 10:22:12 +0000138 if (i.isButton()) {
139 switch (i.customId) {
TheCodedProf01cba762023-02-18 15:55:05 -0500140 case "switch": {
141 await i.deferUpdate();
142 data.enabled = !data.enabled;
143 break;
144 }
145 case "message": {
146 const modal = new ModalBuilder()
147 .setCustomId("modal")
148 .setTitle("Welcome Message")
149 .addComponents(
150 new ActionRowBuilder<TextInputBuilder>().addComponents(
151 new TextInputBuilder()
152 .setCustomId("ex1")
153 .setLabel("Server Info (1/3)")
154 .setPlaceholder(
155 `{serverName} - This server's name\n\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000156 `These placeholders will be replaced with the server's name, etc..`
TheCodedProf01cba762023-02-18 15:55:05 -0500157 )
158 .setMaxLength(1)
159 .setRequired(false)
160 .setStyle(TextInputStyle.Paragraph)
161 ),
162 new ActionRowBuilder<TextInputBuilder>().addComponents(
163 new TextInputBuilder()
164 .setCustomId("ex2")
165 .setLabel("Member Counts (2/3) - {MemberCount:...}")
166 .setPlaceholder(
167 `{:all} - Total member count\n` +
Skyler Greyda16adf2023-03-05 10:22:12 +0000168 `{:humans} - Total non-bot users\n` +
169 `{:bots} - Number of bots\n`
TheCodedProf01cba762023-02-18 15:55:05 -0500170 )
171 .setMaxLength(1)
172 .setRequired(false)
173 .setStyle(TextInputStyle.Paragraph)
174 ),
175 new ActionRowBuilder<TextInputBuilder>().addComponents(
176 new TextInputBuilder()
177 .setCustomId("ex3")
178 .setLabel("Member who joined (3/3) - {member:...}")
Skyler Greyda16adf2023-03-05 10:22:12 +0000179 .setPlaceholder(`{:name} - The members name\n`)
TheCodedProf01cba762023-02-18 15:55:05 -0500180 .setMaxLength(1)
181 .setRequired(false)
182 .setStyle(TextInputStyle.Paragraph)
183 ),
Skyler Greyda16adf2023-03-05 10:22:12 +0000184 new ActionRowBuilder<TextInputBuilder>().addComponents(
185 new TextInputBuilder()
186 .setCustomId("message")
187 .setPlaceholder("Enter a message to send when someone joins the server")
188 .setValue(data.message ?? "")
189 .setLabel("Message")
190 .setStyle(TextInputStyle.Paragraph)
191 )
192 );
193 const button = new ActionRowBuilder<ButtonBuilder>().addComponents(
194 new ButtonBuilder()
195 .setCustomId("back")
196 .setLabel("Back")
197 .setStyle(ButtonStyle.Secondary)
198 .setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
199 );
200 await i.showModal(modal);
TheCodedProf01cba762023-02-18 15:55:05 -0500201 await i.editReply({
202 embeds: [
203 new EmojiEmbed()
204 .setTitle("Welcome Settings")
205 .setDescription("Modal opened. If you can't see it, click back and try again.")
206 .setStatus("Success")
207 ],
208 components: [button]
209 });
210
211 let out: ModalSubmitInteraction | null;
212 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000213 out = (await modalInteractionCollector(m, interaction.user)) as ModalSubmitInteraction | null;
TheCodedProf01cba762023-02-18 15:55:05 -0500214 } catch (e) {
215 console.error(e);
216 out = null;
217 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000218 if (!out) break;
TheCodedProf1807fb32023-02-20 14:33:48 -0500219 data.message = out.fields.getTextInputValue("message");
TheCodedProf01cba762023-02-18 15:55:05 -0500220 break;
221 }
222 case "save": {
223 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000224 await client.database.guilds.write(interaction.guild!.id, { welcome: data });
TheCodedProf01cba762023-02-18 15:55:05 -0500225 config = await client.database.guilds.read(interaction.guild!.id);
226 data = Object.assign({}, config.welcome);
Skyler Greyda16adf2023-03-05 10:22:12 +0000227 await client.memory.forceUpdate(interaction.guild!.id);
TheCodedProf01cba762023-02-18 15:55:05 -0500228 break;
229 }
230 case "channelDM": {
231 await i.deferUpdate();
232 data.channel = "dm";
233 break;
234 }
235 case "role": {
236 await i.deferUpdate();
237 data.role = null;
238 break;
239 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100240 }
TheCodedProf01cba762023-02-18 15:55:05 -0500241 } else if (i.isRoleSelectMenu()) {
242 await i.deferUpdate();
Skyler Greyda16adf2023-03-05 10:22:12 +0000243 switch (i.customId) {
TheCodedProf01cba762023-02-18 15:55:05 -0500244 case "roleToGive": {
245 data.role = i.values[0]!;
Skyler Greyda16adf2023-03-05 10:22:12 +0000246 break;
TheCodedProf01cba762023-02-18 15:55:05 -0500247 }
248 case "roleToPing": {
249 data.ping = i.values[0]!;
Skyler Greyda16adf2023-03-05 10:22:12 +0000250 break;
TheCodedProf01cba762023-02-18 15:55:05 -0500251 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100252 }
TheCodedProf01cba762023-02-18 15:55:05 -0500253 } else {
254 await i.deferUpdate();
255 data.channel = i.values[0]!;
pineafan41d93562022-07-30 22:10:15 +0100256 }
TheCodedProf01cba762023-02-18 15:55:05 -0500257 } while (!closed);
Skyler Greyda16adf2023-03-05 10:22:12 +0000258 await interaction.deleteReply();
pineafan63fc5e22022-08-04 22:04:10 +0100259};
pineafan41d93562022-07-30 22:10:15 +0100260
TheCodedProff86ba092023-01-27 17:10:07 -0500261const check = (interaction: CommandInteraction, _partial: boolean = false) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100262 const member = interaction.member as Discord.GuildMember;
PineaFan538d3752023-01-12 21:48:23 +0000263 if (!member.permissions.has("ManageGuild"))
PineaFan0d06edc2023-01-17 22:10:31 +0000264 return "You must have the *Manage Server* permission to use this command";
pineafan41d93562022-07-30 22:10:15 +0100265 return true;
pineafan63fc5e22022-08-04 22:04:10 +0100266};
pineafan41d93562022-07-30 22:10:15 +0100267
PineaFan538d3752023-01-12 21:48:23 +0000268const autocomplete = async (interaction: AutocompleteInteraction): Promise<string[]> => {
Skyler Greyda16adf2023-03-05 10:22:12 +0000269 const validReplacements = ["serverName", "memberCount:all", "memberCount:bots", "memberCount:humans"];
PineaFan538d3752023-01-12 21:48:23 +0000270 if (!interaction.guild) return [];
271 const prompt = interaction.options.getString("message");
272 const autocompletions = [];
Skyler Greyda16adf2023-03-05 10:22:12 +0000273 if (prompt === null) {
PineaFan538d3752023-01-12 21:48:23 +0000274 for (const replacement of validReplacements) {
275 autocompletions.push(`{${replacement}}`);
Skyler Greyda16adf2023-03-05 10:22:12 +0000276 }
PineaFan538d3752023-01-12 21:48:23 +0000277 return autocompletions;
Skyler Greyda16adf2023-03-05 10:22:12 +0000278 }
PineaFan538d3752023-01-12 21:48:23 +0000279 const beforeLastOpenBracket = prompt.match(/(.*){[^{}]{0,15}$/);
280 const afterLastOpenBracket = prompt.match(/{[^{}]{0,15}$/);
281 if (beforeLastOpenBracket !== null) {
282 if (afterLastOpenBracket !== null) {
283 for (const replacement of validReplacements) {
PineaFan5d98a4b2023-01-19 16:15:47 +0000284 if (replacement.startsWith(afterLastOpenBracket[0]!.slice(1))) {
PineaFan538d3752023-01-12 21:48:23 +0000285 autocompletions.push(`${beforeLastOpenBracket[1]}{${replacement}}`);
286 }
287 }
288 } else {
289 for (const replacement of validReplacements) {
290 autocompletions.push(`${beforeLastOpenBracket[1]}{${replacement}}`);
291 }
292 }
293 } else {
294 for (const replacement of validReplacements) {
295 autocompletions.push(`${prompt} {${replacement}}`);
296 }
297 }
298 return autocompletions;
299};
300
Skyler Grey16ecb172023-03-05 07:30:32 +0000301export { command, callback, check, autocomplete };