blob: 3ba9c25118ebb9d902fcddb47d24fa8089623fd8 [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(
70 new ButtonBuilder().setCustomId("edit").setLabel("Edit Embed").setStyle(ButtonStyle.Secondary),
71 new ButtonBuilder()
72 .setCustomId("send")
73 .setLabel("Send")
74 .setStyle(ButtonStyle.Primary)
75 .setDisabled(!data.channel)
76 );
TheCodedProf46518a42023-02-18 17:08:23 -050077
Skyler Greyda16adf2023-03-05 10:22:12 +000078 const colorSelect = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
79 new StringSelectMenuBuilder()
80 .setCustomId("color")
81 .setPlaceholder("Select a color")
82 .setMinValues(1)
83 .addOptions(
84 Object.keys(colors).map((color: string) => {
85 return new StringSelectMenuOptionBuilder()
86 .setLabel(lodash.capitalize(color))
87 .setValue(color)
88 .setEmoji(getEmojiByName("COLORS." + color, "id") as APIMessageComponentEmoji)
89 .setDefault(data.color === colors[color]);
90 })
91 )
92 );
TheCodedProf46518a42023-02-18 17:08:23 -050093
Skyler Greyda16adf2023-03-05 10:22:12 +000094 const buttonSelect = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
95 new StringSelectMenuBuilder()
96 .setCustomId("button")
97 .setPlaceholder("Select buttons to add")
98 .setMinValues(1)
99 .setMaxValues(3)
100 .addOptions(
101 new StringSelectMenuOptionBuilder()
102 .setLabel("Verify")
103 .setValue("verifybutton")
104 .setDescription("Click to get verified in the server")
105 .setDefault(data.buttons.includes("verifybutton")),
106 new StringSelectMenuOptionBuilder()
107 .setLabel("Role Menu")
108 .setValue("rolemenu")
109 .setDescription("Click to customize your roles")
110 .setDefault(data.buttons.includes("rolemenu")),
111 new StringSelectMenuOptionBuilder()
112 .setLabel("Ticket")
113 .setValue("createticket")
114 .setDescription("Click to create a support ticket")
115 .setDefault(data.buttons.includes("createticket"))
116 )
117 );
TheCodedProf46518a42023-02-18 17:08:23 -0500118
Skyler Greyda16adf2023-03-05 10:22:12 +0000119 const channelMenu = new ActionRowBuilder<ChannelSelectMenuBuilder>().addComponents(
120 new ChannelSelectMenuBuilder()
121 .setCustomId("channel")
122 .setPlaceholder("Select a channel")
123 .setChannelTypes(
124 ChannelType.GuildText,
125 ChannelType.GuildAnnouncement,
126 ChannelType.PublicThread,
127 ChannelType.AnnouncementThread
128 )
129 );
TheCodedProf46518a42023-02-18 17:08:23 -0500130 let channelName = interaction.guild!.channels.cache.get(data.channel!)?.name;
131 if (data.channel === interaction.channelId) channelName = "this channel";
132 const embed = new EmojiEmbed()
133 .setTitle(data.title ?? "No title set")
134 .setDescription(data.description ?? "*No description set*")
135 .setColor(data.color)
Skyler Greyda16adf2023-03-05 10:22:12 +0000136 .setFooter({ text: `Click the button below to edit the embed | The embed will be sent in ${channelName}` });
TheCodedProf46518a42023-02-18 17:08:23 -0500137
138 await interaction.editReply({
139 embeds: [embed],
140 components: [colorSelect, buttonSelect, channelMenu, buttons]
141 });
142
143 let i: Discord.ButtonInteraction | Discord.ChannelSelectMenuInteraction | Discord.StringSelectMenuInteraction;
144 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000145 i = (await interaction.channel!.awaitMessageComponent({
pineafan96228bd2023-02-21 14:22:55 +0000146 filter: (i: Discord.Interaction) => i.user.id === interaction.user.id,
TheCodedProf46518a42023-02-18 17:08:23 -0500147 time: 300000
Skyler Greyda16adf2023-03-05 10:22:12 +0000148 })) as
149 | Discord.ButtonInteraction
150 | Discord.ChannelSelectMenuInteraction
151 | Discord.StringSelectMenuInteraction;
TheCodedProf46518a42023-02-18 17:08:23 -0500152 } catch (e) {
153 closed = true;
154 break;
155 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000156 if (i.isButton()) {
157 switch (i.customId) {
TheCodedProf46518a42023-02-18 17:08:23 -0500158 case "edit": {
159 await i.showModal(
160 new ModalBuilder()
161 .setCustomId("modal")
162 .setTitle(`Options for ${i.customId}`)
163 .addComponents(
164 new ActionRowBuilder<TextInputBuilder>().addComponents(
165 new TextInputBuilder()
166 .setCustomId("title")
167 .setLabel("Title")
168 .setMaxLength(256)
169 .setRequired(false)
170 .setStyle(TextInputStyle.Short)
171 .setValue(data.title ?? "")
172 ),
173 new ActionRowBuilder<TextInputBuilder>().addComponents(
174 new TextInputBuilder()
175 .setCustomId("description")
176 .setLabel("The text to display below the title")
177 .setMaxLength(4000)
178 .setRequired(false)
179 .setStyle(TextInputStyle.Paragraph)
180 .setValue(data.description ?? "")
181 )
182 )
183 );
184 await interaction.editReply({
185 embeds: [
186 new EmojiEmbed()
187 .setTitle("Button Editor")
188 .setDescription("Modal opened. If you can't see it, click back and try again.")
189 .setStatus("Success")
190 .setEmoji("GUILD.TICKET.OPEN")
191 ],
192 components: [
193 new ActionRowBuilder<ButtonBuilder>().addComponents([
194 new ButtonBuilder()
195 .setLabel("Back")
196 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
197 .setStyle(ButtonStyle.Primary)
198 .setCustomId("back")
199 ])
200 ]
201 });
202 let out: Discord.ModalSubmitInteraction | null;
203 try {
Skyler Greyda16adf2023-03-05 10:22:12 +0000204 out = (await modalInteractionCollector(
205 m,
206 interaction.user
207 )) as Discord.ModalSubmitInteraction | null;
TheCodedProf46518a42023-02-18 17:08:23 -0500208 } catch (e) {
209 closed = true;
210 continue;
211 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000212 if (!out || out.isButton()) continue;
213 data.title =
214 out.fields.getTextInputValue("title").length === 0
215 ? null
216 : out.fields.getTextInputValue("title");
217 data.description =
218 out.fields.getTextInputValue("description").length === 0
219 ? null
220 : out.fields.getTextInputValue("description");
TheCodedProf46518a42023-02-18 17:08:23 -0500221 break;
222 }
223 case "send": {
224 await i.deferUpdate();
TheCodedProf1807fb32023-02-20 14:33:48 -0500225 const channel = interaction.guild!.channels.cache.get(data.channel!) as Discord.TextChannel;
226 const components = new ActionRowBuilder<ButtonBuilder>();
Skyler Greyda16adf2023-03-05 10:22:12 +0000227 for (const button of data.buttons) {
TheCodedProf46518a42023-02-18 17:08:23 -0500228 components.addComponents(
229 new ButtonBuilder()
Skyler Greyda16adf2023-03-05 10:22:12 +0000230 .setCustomId(button)
231 .setLabel(buttonNames[button]!)
232 .setStyle(ButtonStyle.Primary)
233 );
234 }
235 const messageData: MessageCreateOptions = { components: [components] };
TheCodedProf46518a42023-02-18 17:08:23 -0500236 if (data.title || data.description) {
Skyler Greyda16adf2023-03-05 10:22:12 +0000237 const e = new EmojiEmbed();
238 if (data.title) e.setTitle(data.title);
239 if (data.description) e.setDescription(data.description);
240 if (data.color) e.setColor(data.color);
TheCodedProf46518a42023-02-18 17:08:23 -0500241 messageData.embeds = [e];
242 }
243 await channel.send(messageData);
244 break;
245 }
246 }
Skyler Greyda16adf2023-03-05 10:22:12 +0000247 } else if (i.isStringSelectMenu()) {
248 try {
249 await i.deferUpdate();
250 } catch (err) {
251 console.log(err);
252 }
253 switch (i.customId) {
TheCodedProf46518a42023-02-18 17:08:23 -0500254 case "color": {
255 data.color = colors[i.values[0]!]!;
256 break;
257 }
258 case "button": {
259 data.buttons = i.values;
260 break;
261 }
262 }
263 } else {
264 await i.deferUpdate();
265 data.channel = i.values[0]!;
266 }
TheCodedProf46518a42023-02-18 17:08:23 -0500267 } while (!closed);
268 await interaction.deleteReply();
Skyler Greyda16adf2023-03-05 10:22:12 +0000269};
TheCodedProf46518a42023-02-18 17:08:23 -0500270
271export const check = (interaction: CommandInteraction, _partial: boolean = false) => {
272 const member = interaction.member as Discord.GuildMember;
273 if (!member.permissions.has("ManageMessages"))
274 return "You must have the *Manage Messages* permission to use this command";
275 return true;
276};