blob: c8053956df46dea66b9cb9acbe26658062453a5b [file] [log] [blame]
Skyler Grey75ea9172022-08-06 10:22:23 +01001import Discord, {
2 CommandInteraction,
Skyler Grey11236ba2022-08-08 21:13:33 +01003 Interaction,
Skyler Grey75ea9172022-08-06 10:22:23 +01004 Message,
5 MessageActionRow,
6 MessageButton,
Skyler Grey11236ba2022-08-08 21:13:33 +01007 MessageComponentInteraction,
8 ModalSubmitInteraction,
Skyler Grey75ea9172022-08-06 10:22:23 +01009 TextInputComponent
10} from "discord.js";
pineafan73a7c4a2022-07-24 10:38:04 +010011import { modalInteractionCollector } from "./dualCollector.js";
pineafan63fc5e22022-08-04 22:04:10 +010012import EmojiEmbed from "./generateEmojiEmbed.js";
pineafan8b4b17f2022-02-27 20:42:52 +000013import getEmojiByName from "./getEmojiByName.js";
pineafan4f164f32022-02-26 22:07:12 +000014
pineafan02ba0232022-07-24 22:16:15 +010015interface CustomBoolean<T> {
16 title: string;
17 disabled: boolean;
18 value: string | null;
pineafan63fc5e22022-08-04 22:04:10 +010019 emoji: string | undefined;
pineafan02ba0232022-07-24 22:16:15 +010020 active: boolean;
21 onClick: () => Promise<T>;
22 response: T | null;
23}
24
pineafan4f164f32022-02-26 22:07:12 +000025class confirmationMessage {
26 interaction: CommandInteraction;
pineafan63fc5e22022-08-04 22:04:10 +010027 title = "";
28 emoji = "";
pineafan62ce1922022-08-25 20:34:45 +010029 redEmoji: string | null = null;
30 timeoutText: string | null = null;
pineafan63fc5e22022-08-04 22:04:10 +010031 description = "";
Skyler Grey75ea9172022-08-06 10:22:23 +010032 color: "Danger" | "Warning" | "Success" = "Success";
33 customButtons: Record<string, CustomBoolean<unknown>> = {};
pineafan63fc5e22022-08-04 22:04:10 +010034 inverted = false;
pineafan73a7c4a2022-07-24 10:38:04 +010035 reason: string | null = null;
pineafan4f164f32022-02-26 22:07:12 +000036
37 constructor(interaction: CommandInteraction) {
38 this.interaction = interaction;
pineafan4f164f32022-02-26 22:07:12 +000039 }
40
Skyler Grey75ea9172022-08-06 10:22:23 +010041 setTitle(title: string) {
42 this.title = title;
43 return this;
44 }
pineafan62ce1922022-08-25 20:34:45 +010045 setEmoji(emoji: string, redEmoji?: string) {
Skyler Grey75ea9172022-08-06 10:22:23 +010046 this.emoji = emoji;
pineafan62ce1922022-08-25 20:34:45 +010047 if (redEmoji) this.redEmoji = redEmoji; // TODO: go through all confirmation messages and change them to use this, and not do their own edits
Skyler Grey75ea9172022-08-06 10:22:23 +010048 return this;
49 }
pineafan62ce1922022-08-25 20:34:45 +010050 setDescription(description: string, timedOut?: string) {
Skyler Grey75ea9172022-08-06 10:22:23 +010051 this.description = description;
pineafan62ce1922022-08-25 20:34:45 +010052 if (timedOut) this.timeoutText = timedOut; // TODO also this
Skyler Grey75ea9172022-08-06 10:22:23 +010053 return this;
54 }
55 setColor(color: "Danger" | "Warning" | "Success") {
56 this.color = color;
57 return this;
58 }
59 setInverted(inverted: boolean) {
60 this.inverted = inverted;
61 return this;
62 }
63 addCustomBoolean(
64 customId: string,
65 title: string,
66 disabled: boolean,
67 callback: () => Promise<unknown> = async () => null,
68 callbackClicked: string | null,
69 emoji?: string,
70 initial?: boolean
71 ) {
72 this.customButtons[customId] = {
73 title: title,
74 disabled: disabled,
75 value: callbackClicked,
76 emoji: emoji,
77 active: initial ?? false,
78 onClick: callback,
79 response: null
80 };
81 return this;
pineafan6fb3e072022-05-20 19:27:23 +010082 }
pineafan73a7c4a2022-07-24 10:38:04 +010083 addReasonButton(reason: string) {
84 this.reason = reason;
85 return this;
86 }
Skyler Grey11236ba2022-08-08 21:13:33 +010087 async send(editOnly?: boolean): Promise<{
88 success?: boolean;
89 cancelled?: boolean;
90 components?: Record<string, CustomBoolean<unknown>>;
91 newReason?: string;
92 }> {
Skyler Greya402d1c2022-08-13 23:18:16 +010093 let cancelled = false;
94 let success: boolean | undefined = undefined;
95 let returnComponents = false;
96 let newReason = undefined;
97
98 while (!cancelled && success === undefined && !returnComponents && !newReason) {
pineafan63fc5e22022-08-04 22:04:10 +010099 const fullComponents = [
pineafan02ba0232022-07-24 22:16:15 +0100100 new Discord.MessageButton()
101 .setCustomId("yes")
102 .setLabel("Confirm")
103 .setStyle(this.inverted ? "SUCCESS" : "DANGER")
104 .setEmoji(getEmojiByName("CONTROL.TICK", "id")),
105 new Discord.MessageButton()
106 .setCustomId("no")
107 .setLabel("Cancel")
108 .setStyle("SECONDARY")
109 .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
pineafan63fc5e22022-08-04 22:04:10 +0100110 ];
pineafan02ba0232022-07-24 22:16:15 +0100111 Object.entries(this.customButtons).forEach(([k, v]) => {
Skyler Grey75ea9172022-08-06 10:22:23 +0100112 const button = new Discord.MessageButton()
pineafan02ba0232022-07-24 22:16:15 +0100113 .setCustomId(k)
114 .setLabel(v.title)
115 .setStyle(v.active ? "SUCCESS" : "PRIMARY")
Skyler Grey75ea9172022-08-06 10:22:23 +0100116 .setDisabled(v.disabled);
Skyler Grey11236ba2022-08-08 21:13:33 +0100117 if (v.emoji !== undefined) button.setEmoji(getEmojiByName(v.emoji, "id"));
Skyler Grey75ea9172022-08-06 10:22:23 +0100118 fullComponents.push(button);
pineafan63fc5e22022-08-04 22:04:10 +0100119 });
Skyler Grey75ea9172022-08-06 10:22:23 +0100120 if (this.reason !== null)
121 fullComponents.push(
122 new Discord.MessageButton()
123 .setCustomId("reason")
124 .setLabel("Edit Reason")
125 .setStyle("PRIMARY")
126 .setEmoji(getEmojiByName("ICONS.EDIT", "id"))
127 .setDisabled(false)
128 );
pineafan63fc5e22022-08-04 22:04:10 +0100129 const components = [];
pineafan02ba0232022-07-24 22:16:15 +0100130 for (let i = 0; i < fullComponents.length; i += 5) {
Skyler Grey11236ba2022-08-08 21:13:33 +0100131 components.push(new MessageActionRow().addComponents(fullComponents.slice(i, i + 5)));
pineafan02ba0232022-07-24 22:16:15 +0100132 }
pineafan63fc5e22022-08-04 22:04:10 +0100133 const object = {
pineafan377794f2022-04-18 19:01:01 +0100134 embeds: [
pineafan4edb7762022-06-26 19:21:04 +0100135 new EmojiEmbed()
pineafan377794f2022-04-18 19:01:01 +0100136 .setEmoji(this.emoji)
137 .setTitle(this.title)
Skyler Grey75ea9172022-08-06 10:22:23 +0100138 .setDescription(
139 this.description +
140 "\n\n" +
141 Object.values(this.customButtons)
142 .map((v) => {
143 if (v.value === null) return "";
144 return v.active ? `*${v.value}*\n` : "";
145 })
146 .join("")
147 )
pineafan377794f2022-04-18 19:01:01 +0100148 .setStatus(this.color)
pineafan377794f2022-04-18 19:01:01 +0100149 ],
pineafan02ba0232022-07-24 22:16:15 +0100150 components: components,
pineafan377794f2022-04-18 19:01:01 +0100151 ephemeral: true,
152 fetchReply: true
pineafan63fc5e22022-08-04 22:04:10 +0100153 };
Skyler Grey75ea9172022-08-06 10:22:23 +0100154 let m: Message;
pineafan02ba0232022-07-24 22:16:15 +0100155 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100156 if (editOnly) {
157 m = (await this.interaction.editReply(object)) as Message;
pineafan02ba0232022-07-24 22:16:15 +0100158 } else {
Skyler Grey11236ba2022-08-08 21:13:33 +0100159 m = (await this.interaction.reply(object)) as unknown as Message;
pineafan02ba0232022-07-24 22:16:15 +0100160 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100161 } catch {
Skyler Greya402d1c2022-08-13 23:18:16 +0100162 cancelled = true;
163 continue;
Skyler Grey75ea9172022-08-06 10:22:23 +0100164 }
pineafan377794f2022-04-18 19:01:01 +0100165 let component;
166 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100167 component = await m.awaitMessageComponent({
168 filter: (m) => m.user.id === this.interaction.user.id,
169 time: 300000
170 });
pineafan377794f2022-04-18 19:01:01 +0100171 } catch (e) {
Skyler Greya402d1c2022-08-13 23:18:16 +0100172 success = false;
173 returnComponents = true;
174 continue;
pineafan377794f2022-04-18 19:01:01 +0100175 }
176 if (component.customId === "yes") {
177 component.deferUpdate();
pineafan63fc5e22022-08-04 22:04:10 +0100178 for (const v of Object.values(this.customButtons)) {
179 if (!v.active) continue;
Skyler Grey75ea9172022-08-06 10:22:23 +0100180 try {
181 v.response = await v.onClick();
182 } catch (e) {
183 console.log(e);
184 }
pineafan63fc5e22022-08-04 22:04:10 +0100185 }
Skyler Greya402d1c2022-08-13 23:18:16 +0100186 success = true;
187 returnComponents = true;
188 continue;
pineafan377794f2022-04-18 19:01:01 +0100189 } else if (component.customId === "no") {
190 component.deferUpdate();
Skyler Greya402d1c2022-08-13 23:18:16 +0100191 success = false;
192 returnComponents = true;
193 continue;
pineafan73a7c4a2022-07-24 10:38:04 +0100194 } else if (component.customId === "reason") {
Skyler Grey75ea9172022-08-06 10:22:23 +0100195 await component.showModal(
196 new Discord.Modal()
197 .setCustomId("modal")
198 .setTitle("Editing reason")
199 .addComponents(
200 new MessageActionRow<TextInputComponent>().addComponents(
201 new TextInputComponent()
202 .setCustomId("reason")
203 .setLabel("Reason")
204 .setMaxLength(2000)
205 .setRequired(false)
206 .setStyle("PARAGRAPH")
207 .setPlaceholder("Spammed in #general")
208 .setValue(this.reason ? this.reason : "")
209 )
210 )
211 );
pineafan73a7c4a2022-07-24 10:38:04 +0100212 await this.interaction.editReply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100213 embeds: [
214 new EmojiEmbed()
215 .setTitle(this.title)
Skyler Grey11236ba2022-08-08 21:13:33 +0100216 .setDescription("Modal opened. If you can't see it, click back and try again.")
Skyler Grey75ea9172022-08-06 10:22:23 +0100217 .setStatus(this.color)
218 .setEmoji(this.emoji)
219 ],
220 components: [
221 new MessageActionRow().addComponents([
222 new MessageButton()
223 .setLabel("Back")
224 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
225 .setStyle("PRIMARY")
226 .setCustomId("back")
227 ])
228 ]
pineafan73a7c4a2022-07-24 10:38:04 +0100229 });
230 let out;
231 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100232 out = await modalInteractionCollector(
233 m,
Skyler Grey11236ba2022-08-08 21:13:33 +0100234 (m: Interaction) =>
235 (m as MessageComponentInteraction | ModalSubmitInteraction).channelId ===
236 this.interaction.channelId,
Skyler Grey75ea9172022-08-06 10:22:23 +0100237 (m) => m.customId === "reason"
238 );
239 } catch (e) {
Skyler Greya402d1c2022-08-13 23:18:16 +0100240 cancelled = true;
241 continue;
Skyler Grey75ea9172022-08-06 10:22:23 +0100242 }
Skyler Grey11236ba2022-08-08 21:13:33 +0100243 if (out === null) {
Skyler Greya402d1c2022-08-13 23:18:16 +0100244 cancelled = true;
245 continue;
Skyler Grey11236ba2022-08-08 21:13:33 +0100246 }
247 if (out instanceof ModalSubmitInteraction) {
Skyler Greya402d1c2022-08-13 23:18:16 +0100248 newReason = out.fields.getTextInputValue("reason");
249 continue;
Skyler Grey75ea9172022-08-06 10:22:23 +0100250 } else {
Skyler Greya402d1c2022-08-13 23:18:16 +0100251 returnComponents = true;
252 continue;
Skyler Grey75ea9172022-08-06 10:22:23 +0100253 }
pineafan02ba0232022-07-24 22:16:15 +0100254 } else {
255 component.deferUpdate();
Skyler Grey11236ba2022-08-08 21:13:33 +0100256 this.customButtons[component.customId]!.active = !this.customButtons[component.customId]!.active;
Skyler Greya402d1c2022-08-13 23:18:16 +0100257 returnComponents = true;
258 continue;
pineafan377794f2022-04-18 19:01:01 +0100259 }
pineafan8b4b17f2022-02-27 20:42:52 +0000260 }
Skyler Greya402d1c2022-08-13 23:18:16 +0100261 const returnValue: Awaited<ReturnType<typeof this.send>> = {};
262
pineafan62ce1922022-08-25 20:34:45 +0100263 if (returnComponents || success !== undefined) returnValue.components = this.customButtons;
Skyler Greya402d1c2022-08-13 23:18:16 +0100264 if (success !== undefined) returnValue.success = success;
pineafan62ce1922022-08-25 20:34:45 +0100265 if (cancelled) {
266 await this.timeoutError()
267 returnValue.cancelled = true;
268 }
Skyler Greya402d1c2022-08-13 23:18:16 +0100269 if (newReason) returnValue.newReason = newReason;
270
271 return returnValue;
pineafan4f164f32022-02-26 22:07:12 +0000272 }
pineafan62ce1922022-08-25 20:34:45 +0100273
274 async timeoutError(): Promise<void> {
275 await this.interaction.editReply({
276 embeds: [
277 new EmojiEmbed()
278 .setTitle(this.title)
279 .setDescription("We closed this message because it was not used for a while.")
280 .setStatus("Danger")
281 .setEmoji(this.redEmoji ?? this.emoji)
282 ],
283 components: []
284 })
285 }
pineafan4f164f32022-02-26 22:07:12 +0000286}
287
pineafan73a7c4a2022-07-24 10:38:04 +0100288export default confirmationMessage;