blob: 3aa22149f44a829c40aa15b14287797aeed83b6c [file] [log] [blame]
Skyler Greyda16adf2023-03-05 10:22:12 +00001import {
2 ActionRowBuilder,
3 APIMessageComponentEmoji,
4 ButtonBuilder,
5 ButtonStyle,
6 ChannelSelectMenuBuilder,
7 ChannelType,
8 CommandInteraction,
9 MessageCreateOptions,
10 ModalBuilder,
11 SlashCommandSubcommandBuilder,
12 StringSelectMenuBuilder,
13 StringSelectMenuOptionBuilder,
14 TextInputBuilder,
15 TextInputStyle
16} from "discord.js";
TheCodedProf46518a42023-02-18 17:08:23 -050017import type Discord from "discord.js";
18import { LoadingEmbed } from "../../utils/defaults.js";
19import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
20import lodash from "lodash";
21import getEmojiByName from "../../utils/getEmojiByName.js";
22import { modalInteractionCollector } from "../../utils/dualCollector.js";
23
24export const command = new SlashCommandSubcommandBuilder()
25 .setName("buttons")
26 .setDescription("Create clickable buttons for verifying, role menus etc.");
27
28interface Data {
Skyler Greyda16adf2023-03-05 10:22:12 +000029 buttons: string[];
30 title: string | null;
31 description: string | null;
32 color: number;
33 channel: string | null;
TheCodedProf46518a42023-02-18 17:08:23 -050034}
35
Skyler Greyda16adf2023-03-05 10:22:12 +000036const colors: Record<string, number> = {
37 RED: 0xf27878,
38 ORANGE: 0xe5ab71,
39 YELLOW: 0xf2d478,
40 GREEN: 0x65cc76,
41 BLUE: 0x72aef5,
42 PURPLE: 0xa358b2,
43 PINK: 0xd46899,
44 GRAY: 0x999999
45};
TheCodedProf46518a42023-02-18 17:08:23 -050046
47const buttonNames: Record<string, string> = {
48 verifybutton: "Verify",
49 rolemenu: "Role Menu",
TheCodedProf9c51a7e2023-02-27 17:11:13 -050050 createticket: "Create Ticket"
Skyler Greyda16adf2023-03-05 10:22:12 +000051};
TheCodedProf46518a42023-02-18 17:08:23 -050052
53export const callback = async (interaction: CommandInteraction): Promise<void> => {
TheCodedProf46518a42023-02-18 17:08:23 -050054 const m = await interaction.reply({
55 embeds: LoadingEmbed,
56 fetchReply: true,
57 ephemeral: true
58 });
59
60 let closed = false;
TheCodedProf1807fb32023-02-20 14:33:48 -050061 const data: Data = {
TheCodedProf46518a42023-02-18 17:08:23 -050062 buttons: [],
63 title: null,
64 description: null,
65 color: colors["RED"]!,
66 channel: interaction.channelId
Skyler Greyda16adf2023-03-05 10:22:12 +000067 };
TheCodedProf46518a42023-02-18 17:08:23 -050068 do {
Skyler Greyda16adf2023-03-05 10:22:12 +000069 const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents(
TheCodedProf80ad8542023-03-10 12:52:33 -050070 new ButtonBuilder()
71 .setCustomId("edit")
72 .setLabel("Edit Embed")
73 .setStyle(ButtonStyle.Secondary)
74 .setEmoji(getEmojiByName("ICONS.EDIT") as APIMessageComponentEmoji)
75 ,
Skyler Greyda16adf2023-03-05 10:22:12 +000076 new ButtonBuilder()
77 .setCustomId("send")
78 .setLabel("Send")
79 .setStyle(ButtonStyle.Primary)
80 .setDisabled(!data.channel)
81 );
TheCodedProf46518a42023-02-18 17:08:23 -050082
Skyler Greyda16adf2023-03-05 10:22:12 +000083 const colorSelect = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
84 new StringSelectMenuBuilder()
85 .setCustomId("color")
86 .setPlaceholder("Select a color")
87 .setMinValues(1)
88 .addOptions(
89 Object.keys(colors).map((color: string) => {
90 return new StringSelectMenuOptionBuilder()
91 .setLabel(lodash.capitalize(color))
92 .setValue(color)
93 .setEmoji(getEmojiByName("COLORS." + color, "id") as APIMessageComponentEmoji)
94 .setDefault(data.color === colors[color]);
95 })
96 )
97 );
TheCodedProf46518a42023-02-18 17:08:23 -050098
Skyler Greyda16adf2023-03-05 10:22:12 +000099 const buttonSelect = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
100 new StringSelectMenuBuilder()
101 .setCustomId("button")
102 .setPlaceholder("Select buttons to add")
103 .setMinValues(1)
104 .setMaxValues(3)
105 .addOptions(
106 new StringSelectMenuOptionBuilder()
107 .setLabel("Verify")
108 .setValue("verifybutton")
109 .setDescription("Click to get verified in the server")
110 .setDefault(data.buttons.includes("verifybutton")),
111 new StringSelectMenuOptionBuilder()
112 .setLabel("Role Menu")
113 .setValue("rolemenu")
114 .setDescription("Click to customize your roles")
115 .setDefault(data.buttons.includes("rolemenu")),
116 new StringSelectMenuOptionBuilder()
117 .setLabel("Ticket")
118 .setValue("createticket")
119 .setDescription("Click to create a support ticket")
120 .setDefault(data.buttons.includes("createticket"))
121 )
122 );
TheCodedProf46518a42023-02-18 17:08:23 -0500123
Skyler Greyda16adf2023-03-05 10:22:12 +0000124 const channelMenu = new ActionRowBuilder<ChannelSelectMenuBuilder>().addComponents(
125 new ChannelSelectMenuBuilder()
126 .setCustomId("channel")
127 .setPlaceholder("Select a channel")
128 .setChannelTypes(
129 ChannelType.GuildText,
130 ChannelType.GuildAnnouncement,
131 ChannelType.PublicThread,
132 ChannelType.AnnouncementThread
133 )
134 );
TheCodedProf46518a42023-02-18 17:08:23 -0500135 let channelName = interaction.guild!.channels.cache.get(data.channel!)?.name;
136 if (data.channel === interaction.channelId) channelName = "this channel";
137 const embed = new EmojiEmbed()
138 .setTitle(data.title ?? "No title set")
139 .setDescription(data.description ?? "*No description set*")
140 .setColor(data.color)
Skyler Greyda16adf2023-03-05 10:22:12 +0000141 .setFooter({ text: `Click the button below to edit the embed | The embed will be sent in ${channelName}` });
TheCodedProf46518a42023-02-18 17:08:23 -0500142
143 await interaction.editReply({
144 embeds: [embed],
145 components: [colorSelect, buttonSelect, channelMenu, buttons]
146 });
147
148 let i: Discord.ButtonInteraction | Discord.ChannelSelectMenuInteraction | Discord.StringSelectMenuInteraction;
149 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000150 i = (await interaction.channel!.awaitMessageComponent({
TheCodedProf80ad8542023-03-10 12:52:33 -0500151 filter: (i: Discord.Interaction) => i.user.id === interaction.user.id && i.isMessageComponent() && i.message.id === m.id,
TheCodedProf46518a42023-02-18 17:08:23 -0500152 time: 300000
Skyler Greyda16adf2023-03-05 10:22:12 +0000153 })) as
154 | Discord.ButtonInteraction
155 | Discord.ChannelSelectMenuInteraction
156 | Discord.StringSelectMenuInteraction;
TheCodedProf46518a42023-02-18 17:08:23 -0500157 } catch (e) {
158 closed = true;
159 break;
160 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000161 if (i.isButton()) {
162 switch (i.customId) {
TheCodedProf46518a42023-02-18 17:08:23 -0500163 case "edit": {
164 await i.showModal(
165 new ModalBuilder()
166 .setCustomId("modal")
167 .setTitle(`Options for ${i.customId}`)
168 .addComponents(
169 new ActionRowBuilder<TextInputBuilder>().addComponents(
170 new TextInputBuilder()
171 .setCustomId("title")
172 .setLabel("Title")
173 .setMaxLength(256)
174 .setRequired(false)
175 .setStyle(TextInputStyle.Short)
176 .setValue(data.title ?? "")
177 ),
178 new ActionRowBuilder<TextInputBuilder>().addComponents(
179 new TextInputBuilder()
180 .setCustomId("description")
181 .setLabel("The text to display below the title")
182 .setMaxLength(4000)
183 .setRequired(false)
184 .setStyle(TextInputStyle.Paragraph)
185 .setValue(data.description ?? "")
186 )
187 )
188 );
189 await interaction.editReply({
190 embeds: [
191 new EmojiEmbed()
192 .setTitle("Button Editor")
193 .setDescription("Modal opened. If you can't see it, click back and try again.")
194 .setStatus("Success")
195 .setEmoji("GUILD.TICKET.OPEN")
196 ],
197 components: [
198 new ActionRowBuilder<ButtonBuilder>().addComponents([
199 new ButtonBuilder()
200 .setLabel("Back")
201 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
202 .setStyle(ButtonStyle.Primary)
203 .setCustomId("back")
204 ])
205 ]
206 });
207 let out: Discord.ModalSubmitInteraction | null;
208 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000209 out = (await modalInteractionCollector(
210 m,
211 interaction.user
212 )) as Discord.ModalSubmitInteraction | null;
TheCodedProf46518a42023-02-18 17:08:23 -0500213 } catch (e) {
214 closed = true;
215 continue;
216 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000217 if (!out || out.isButton()) continue;
218 data.title =
219 out.fields.getTextInputValue("title").length === 0
220 ? null
221 : out.fields.getTextInputValue("title");
222 data.description =
223 out.fields.getTextInputValue("description").length === 0
224 ? null
225 : out.fields.getTextInputValue("description");
TheCodedProf46518a42023-02-18 17:08:23 -0500226 break;
227 }
228 case "send": {
229 await i.deferUpdate();
TheCodedProf1807fb32023-02-20 14:33:48 -0500230 const channel = interaction.guild!.channels.cache.get(data.channel!) as Discord.TextChannel;
TheCodedProf80ad8542023-03-10 12:52:33 -0500231 const messageData: MessageCreateOptions = { };
Skyler Greyda16adf2023-03-05 10:22:12 +0000232 for (const button of data.buttons) {
TheCodedProf80ad8542023-03-10 12:52:33 -0500233 messageData.components = [new ActionRowBuilder<ButtonBuilder>().addComponents(
TheCodedProf46518a42023-02-18 17:08:23 -0500234 new ButtonBuilder()
Skyler Greyda16adf2023-03-05 10:22:12 +0000235 .setCustomId(button)
236 .setLabel(buttonNames[button]!)
237 .setStyle(ButtonStyle.Primary)
TheCodedProf80ad8542023-03-10 12:52:33 -0500238 )];
Skyler Greyda16adf2023-03-05 10:22:12 +0000239 }
TheCodedProf80ad8542023-03-10 12:52:33 -0500240 if (data.title || data.description || data.color) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000241 const e = new EmojiEmbed();
242 if (data.title) e.setTitle(data.title);
243 if (data.description) e.setDescription(data.description);
244 if (data.color) e.setColor(data.color);
TheCodedProf46518a42023-02-18 17:08:23 -0500245 messageData.embeds = [e];
246 }
247 await channel.send(messageData);
248 break;
249 }
250 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000251 } else if (i.isStringSelectMenu()) {
252 try {
253 await i.deferUpdate();
254 } catch (err) {
255 console.log(err);
256 }
257 switch (i.customId) {
TheCodedProf46518a42023-02-18 17:08:23 -0500258 case "color": {
259 data.color = colors[i.values[0]!]!;
260 break;
261 }
262 case "button": {
263 data.buttons = i.values;
264 break;
265 }
266 }
267 } else {
268 await i.deferUpdate();
269 data.channel = i.values[0]!;
270 }
TheCodedProf46518a42023-02-18 17:08:23 -0500271 } while (!closed);
272 await interaction.deleteReply();
Skyler Greyda16adf2023-03-05 10:22:12 +0000273};
TheCodedProf46518a42023-02-18 17:08:23 -0500274
275export const check = (interaction: CommandInteraction, _partial: boolean = false) => {
276 const member = interaction.member as Discord.GuildMember;
277 if (!member.permissions.has("ManageMessages"))
278 return "You must have the *Manage Messages* permission to use this command";
279 return true;
280};