blob: b539aac98e4207612d1d6609b3728c404e42dbd5 [file] [log] [blame]
PineaFan0d06edc2023-01-17 22:10:31 +00001import { LoadingEmbed } from "../utils/defaults.js";
pineafan3a02ea32022-08-11 21:35:04 +01002import Discord, {
TheCodedProf21c08592022-09-13 14:14:43 -04003 ActionRowBuilder,
4 ButtonBuilder,
pineafan3a02ea32022-08-11 21:35:04 +01005 MessageComponentInteraction,
6 MessageSelectOptionData,
7 Guild,
8 CommandInteraction,
9 GuildTextBasedChannel,
10 Message,
TheCodedProf21c08592022-09-13 14:14:43 -040011 SelectMenuInteraction,
12 ButtonStyle
pineafan3a02ea32022-08-11 21:35:04 +010013} from "discord.js";
pineafan813bdf42022-07-24 10:39:10 +010014import EmojiEmbed from "../utils/generateEmojiEmbed.js";
15import getEmojiByName from "../utils/getEmojiByName.js";
16import createPageIndicator from "../utils/createPageIndicator.js";
pineafan813bdf42022-07-24 10:39:10 +010017
18class Embed {
TheCodedProf21c08592022-09-13 14:14:43 -040019 embed: Discord.EmbedBuilder;
pineafan813bdf42022-07-24 10:39:10 +010020 title: string;
pineafan63fc5e22022-08-04 22:04:10 +010021 description = "";
22 pageId = 0;
pineafan3a02ea32022-08-11 21:35:04 +010023
24 constructor() {
TheCodedProf21c08592022-09-13 14:14:43 -040025 this.embed = new Discord.EmbedBuilder();
pineafan3a02ea32022-08-11 21:35:04 +010026 this.title = "";
27 }
TheCodedProf21c08592022-09-13 14:14:43 -040028 setEmbed(embed: Discord.EmbedBuilder) {
Skyler Grey75ea9172022-08-06 10:22:23 +010029 this.embed = embed;
30 return this;
31 }
32 setTitle(title: string) {
33 this.title = title;
34 return this;
35 }
36 setDescription(description: string) {
37 this.description = description;
38 return this;
39 }
40 setPageId(pageId: number) {
41 this.pageId = pageId;
42 return this;
43 }
pineafan813bdf42022-07-24 10:39:10 +010044}
45
pineafan3a02ea32022-08-11 21:35:04 +010046export default async (guild: Guild, interaction?: CommandInteraction) => {
47 let c: GuildTextBasedChannel | null = guild.publicUpdatesChannel ? guild.publicUpdatesChannel : guild.systemChannel;
Skyler Grey75ea9172022-08-06 10:22:23 +010048 c = c
49 ? c
pineafan3a02ea32022-08-11 21:35:04 +010050 : (guild.channels.cache.find(
Skyler Grey75ea9172022-08-06 10:22:23 +010051 (ch) =>
pineafan3a02ea32022-08-11 21:35:04 +010052 [
53 "GUILD_TEXT",
54 "GUILD_NEWS",
55 "GUILD_NEWS_THREAD",
56 "GUILD_PRIVATE_THREAD",
57 "GUILD_PUBLIC_THREAD"
58 ].includes(ch.type) &&
Skyler Grey11236ba2022-08-08 21:13:33 +010059 ch.permissionsFor(guild.roles.everyone).has("SEND_MESSAGES") &&
pineafan3a02ea32022-08-11 21:35:04 +010060 ch.permissionsFor(guild.me!).has("EMBED_LINKS")
61 ) as GuildTextBasedChannel | undefined) ?? null;
62 if (interaction) c = interaction.channel as GuildTextBasedChannel;
63 if (!c) {
64 return;
65 }
pineafan63fc5e22022-08-04 22:04:10 +010066 const pages = [
pineafan813bdf42022-07-24 10:39:10 +010067 new Embed()
Skyler Grey75ea9172022-08-06 10:22:23 +010068 .setEmbed(
69 new EmojiEmbed()
70 .setTitle("Welcome to Nucleus")
71 .setDescription(
72 "Thanks for adding Nucleus to your server\n\n" +
73 "On the next few pages you can find instructions on getting started, and commands you may want to set up\n\n" +
74 "If you need support, have questions or want features, you can let us know in [Clicks](https://discord.gg/bPaNnxe)"
75 )
76 .setEmoji("NUCLEUS.LOGO")
77 .setStatus("Danger")
78 )
79 .setTitle("Welcome")
80 .setDescription("About Nucleus")
81 .setPageId(0),
pineafan813bdf42022-07-24 10:39:10 +010082 new Embed()
Skyler Grey75ea9172022-08-06 10:22:23 +010083 .setEmbed(
84 new EmojiEmbed()
85 .setTitle("Logging")
86 .setDescription(
87 "Nucleus can log server events and keep you informed with what content is being posted to your server.\n" +
88 "We have 2 different types of logs, which each can be configured to send to a channel of your choice:\n" +
89 "**General Logs:** These are events like kicks and channel changes etc.\n" +
90 "**Warning Logs:** Warnings like NSFW avatars and spam etc. that may require action by a server staff member. " +
91 "These go to to a separate staff notifications channel.\n\n" +
92 "A general log channel can be set with `/settings log`\n" +
93 "A warning log channel can be set with `/settings warnings channel`"
94 )
95 .setEmoji("ICONS.LOGGING")
96 .setStatus("Danger")
97 )
98 .setTitle("Logging")
99 .setDescription("Logging, staff warning logs etc.")
100 .setPageId(1),
pineafan813bdf42022-07-24 10:39:10 +0100101 new Embed()
Skyler Grey75ea9172022-08-06 10:22:23 +0100102 .setEmbed(
103 new EmojiEmbed()
104 .setTitle("Moderation")
105 .setDescription(
106 "Nucleus has a number of commands that can be used to moderate your server.\n" +
107 "These commands are all found under `/mod`, and they include:\n" +
108 `**${getEmojiByName(
109 "PUNISH.WARN.YELLOW"
110 )} Warn:** The user is warned (via DM) that they violated server rules.\n` +
111 `**${getEmojiByName(
112 "PUNISH.CLEARHISTORY"
113 )} Clear:** Some messages from a user are deleted in a channel.\n` +
114 `**${getEmojiByName(
115 "PUNISH.MUTE.YELLOW"
116 )} Mute:** The user is unable to send messages or join voice chats.\n` +
117 `**${getEmojiByName(
118 "PUNISH.MUTE.GREEN"
119 )} Unmute:** The user is able to send messages in the server.\n` +
Skyler Grey11236ba2022-08-08 21:13:33 +0100120 `**${getEmojiByName("PUNISH.KICK.RED")} Kick:** The user is removed from the server.\n` +
Skyler Grey75ea9172022-08-06 10:22:23 +0100121 `**${getEmojiByName(
122 "PUNISH.SOFTBAN"
123 )} Softban:** Kicks the user, deleting their messages from every channel.\n` +
124 `**${getEmojiByName(
125 "PUNISH.BAN.RED"
126 )} Ban:** The user is removed from the server, and they are unable to rejoin.\n` +
Skyler Grey11236ba2022-08-08 21:13:33 +0100127 `**${getEmojiByName("PUNISH.BAN.GREEN")} Unban:** The user is able to rejoin the server.`
Skyler Grey75ea9172022-08-06 10:22:23 +0100128 )
129 .setEmoji("PUNISH.BAN.RED")
130 .setStatus("Danger")
131 )
132 .setTitle("Moderation")
133 .setDescription("Basic moderation commands")
134 .setPageId(2),
pineafan813bdf42022-07-24 10:39:10 +0100135 new Embed()
Skyler Grey75ea9172022-08-06 10:22:23 +0100136 .setEmbed(
137 new EmojiEmbed()
138 .setTitle("Verify")
139 .setDescription(
140 "Nucleus has a verification system that allows users to prove they aren't bots.\n" +
141 "This is done by running `/verify` which sends a message only the user can see, giving them a link to a CAPTCHA to verify.\n" +
142 "After the user complete's the CAPTCHA, they are given a role and can use the permissions accordingly.\n" +
143 "You can set the role given with `/settings verify`"
144 )
145 .setEmoji("CONTROL.REDTICK")
146 .setStatus("Danger")
147 )
148 .setTitle("Verify")
149 .setDescription("Captcha verification system")
150 .setPageId(3),
pineafan813bdf42022-07-24 10:39:10 +0100151 new Embed()
Skyler Grey75ea9172022-08-06 10:22:23 +0100152 .setEmbed(
153 new EmojiEmbed()
154 .setTitle("Content Scanning")
155 .setDescription(
156 "Nucleus has a content scanning system that automatically scans links and images sent by users.\n" +
157 "Nucleus can detect, delete, and punish users for sending NSFW content, or links to scam or adult sites.\n" +
158 "You can set the threshold for this in `/settings automation`" // TODO
159 )
160 .setEmoji("MOD.IMAGES.TOOSMALL")
161 .setStatus("Danger")
162 )
163 .setTitle("Content Scanning")
164 .setDescription("Content (NSFW, malware, scams) scanning")
165 .setPageId(4),
pineafan813bdf42022-07-24 10:39:10 +0100166 new Embed()
Skyler Grey75ea9172022-08-06 10:22:23 +0100167 .setEmbed(
168 new EmojiEmbed()
169 .setTitle("Tickets")
170 .setDescription(
171 "Nucleus has a ticket system that allows users to create tickets and have a support team respond to them.\n" +
172 "Tickets can be created with `/ticket create` and a channel is created, pinging the user and support role.\n" +
173 "When the ticket is resolved, anyone can run `/ticket close` (or click the button) to close it.\n" +
174 "Running `/ticket close` again will delete the ticket."
175 )
176 .setEmoji("GUILD.TICKET.CLOSE")
177 .setStatus("Danger")
178 )
179 .setTitle("Tickets")
180 .setDescription("Ticket system")
181 .setPageId(5),
pineafan813bdf42022-07-24 10:39:10 +0100182 new Embed()
Skyler Grey75ea9172022-08-06 10:22:23 +0100183 .setEmbed(
184 new EmojiEmbed()
185 .setTitle("Tags")
186 .setDescription(
187 "Add a tag system to your server with the `/tag` and `/tags` commands.\n" +
188 "To create a tag, type `/tags create <tag name> <tag content>`.\n" +
189 "Tag names and content can be edited with `/tags edit`.\n" +
190 "To delete a tag, type `/tags delete <tag name>`.\n" +
191 "To view all tags, type `/tags list`.\n"
192 )
193 .setEmoji("PUNISH.NICKNAME.RED")
194 .setStatus("Danger")
195 )
196 .setTitle("Tags")
197 .setDescription("Tag system")
198 .setPageId(6),
pineafan813bdf42022-07-24 10:39:10 +0100199 new Embed()
Skyler Grey75ea9172022-08-06 10:22:23 +0100200 .setEmbed(
201 new EmojiEmbed()
202 .setTitle("Premium")
203 .setDescription(
204 "In the near future, we will be releasing extra premium only features.\n" +
205 "These features will include:\n\n" +
206 "**Attachment logs**\n> When a message with attachments is edited or deleted, the logs will also include the images sent.\n" +
207 "\nPremium is not yet available. Check `/nucleus premium` for updates on features and pricing"
208 )
209 .setEmoji("NUCLEUS.COMMANDS.LOCK")
210 .setStatus("Danger")
211 )
212 .setTitle("Premium")
213 .setDescription("Premium features")
214 .setPageId(7)
pineafan63fc5e22022-08-04 22:04:10 +0100215 ];
pineafan3a02ea32022-08-11 21:35:04 +0100216 let m: Message;
Skyler Grey75ea9172022-08-06 10:22:23 +0100217 if (interaction) {
pineafan3a02ea32022-08-11 21:35:04 +0100218 m = (await interaction.reply({
Skyler Grey75ea9172022-08-06 10:22:23 +0100219 embeds: LoadingEmbed,
220 fetchReply: true,
221 ephemeral: true
pineafan3a02ea32022-08-11 21:35:04 +0100222 })) as Message;
Skyler Grey75ea9172022-08-06 10:22:23 +0100223 } else {
224 m = await c.send({ embeds: LoadingEmbed });
225 }
pineafan813bdf42022-07-24 10:39:10 +0100226 let page = 0;
227
PineaFan0d06edc2023-01-17 22:10:31 +0000228 const publicFilter = async (component: MessageComponentInteraction) => {
Skyler Grey11236ba2022-08-08 21:13:33 +0100229 return (component.member as Discord.GuildMember).permissions.has("MANAGE_GUILD");
pineafan63fc5e22022-08-04 22:04:10 +0100230 };
pineafan813bdf42022-07-24 10:39:10 +0100231
232 let selectPaneOpen = false;
233
Skyler Greyf21323a2022-08-13 23:58:22 +0100234 let cancelled = false;
235 let timedOut = false;
236 while (!cancelled && !timedOut) {
TheCodedProf21c08592022-09-13 14:14:43 -0400237 let selectPane: ActionRowBuilder[] = [];
pineafan813bdf42022-07-24 10:39:10 +0100238
239 if (selectPaneOpen) {
pineafan3a02ea32022-08-11 21:35:04 +0100240 const options: MessageSelectOptionData[] = [];
Skyler Grey75ea9172022-08-06 10:22:23 +0100241 pages.forEach((embed) => {
pineafan3a02ea32022-08-11 21:35:04 +0100242 options.push({
243 label: embed.title,
244 value: embed.pageId.toString(),
245 description: embed.description || ""
246 });
pineafan63fc5e22022-08-04 22:04:10 +0100247 });
Skyler Grey75ea9172022-08-06 10:22:23 +0100248 selectPane = [
TheCodedProf21c08592022-09-13 14:14:43 -0400249 new ActionRowBuilder().addComponents([
250 new Discord.SelectMenuBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100251 .addOptions(options)
252 .setCustomId("page")
253 .setMaxValues(1)
254 .setPlaceholder("Choose a page...")
255 ])
256 ];
pineafan813bdf42022-07-24 10:39:10 +0100257 }
Skyler Grey75ea9172022-08-06 10:22:23 +0100258 const components = selectPane.concat([
TheCodedProf21c08592022-09-13 14:14:43 -0400259 new ActionRowBuilder().addComponents([
260 new ButtonBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100261 .setCustomId("left")
262 .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
TheCodedProf21c08592022-09-13 14:14:43 -0400263 .setStyle(ButtonStyle.Secondary)
Skyler Grey75ea9172022-08-06 10:22:23 +0100264 .setDisabled(page === 0),
TheCodedProf21c08592022-09-13 14:14:43 -0400265 new ButtonBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100266 .setCustomId("select")
267 .setEmoji(getEmojiByName("CONTROL.MENU", "id"))
TheCodedProf21c08592022-09-13 14:14:43 -0400268 .setStyle(selectPaneOpen ? ButtonStyle.Primary : ButtonStyle.Secondary)
Skyler Grey75ea9172022-08-06 10:22:23 +0100269 .setDisabled(false),
TheCodedProf21c08592022-09-13 14:14:43 -0400270 new ButtonBuilder()
Skyler Grey75ea9172022-08-06 10:22:23 +0100271 .setCustomId("right")
272 .setEmoji(getEmojiByName("CONTROL.RIGHT", "id"))
TheCodedProf21c08592022-09-13 14:14:43 -0400273 .setStyle(ButtonStyle.Secondary)
Skyler Grey75ea9172022-08-06 10:22:23 +0100274 .setDisabled(page === pages.length - 1)
275 ])
276 ]);
pineafan813bdf42022-07-24 10:39:10 +0100277 if (interaction) {
TheCodedProf21c08592022-09-13 14:14:43 -0400278 const em = new Discord.EmbedBuilder(pages[page]!.embed);
Skyler Grey11236ba2022-08-08 21:13:33 +0100279 em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page));
pineafan813bdf42022-07-24 10:39:10 +0100280 await interaction.editReply({
281 embeds: [em],
282 components: components
283 });
284 } else {
TheCodedProf21c08592022-09-13 14:14:43 -0400285 const em = new Discord.EmbedBuilder(pages[page]!.embed);
Skyler Grey11236ba2022-08-08 21:13:33 +0100286 em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page));
pineafan3a02ea32022-08-11 21:35:04 +0100287 (await m.edit({
pineafan813bdf42022-07-24 10:39:10 +0100288 embeds: [em],
pineafan3a02ea32022-08-11 21:35:04 +0100289 components: components
290 })) as Message;
pineafan813bdf42022-07-24 10:39:10 +0100291 }
pineafan63fc5e22022-08-04 22:04:10 +0100292 let i;
pineafan813bdf42022-07-24 10:39:10 +0100293 try {
Skyler Grey75ea9172022-08-06 10:22:23 +0100294 i = await m.awaitMessageComponent({
295 filter: interaction
PineaFan0d06edc2023-01-17 22:10:31 +0000296 ? (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
297 : publicFilter,
Skyler Grey75ea9172022-08-06 10:22:23 +0100298 time: 300000
299 });
300 } catch (e) {
Skyler Greyf21323a2022-08-13 23:58:22 +0100301 timedOut = true;
302 continue;
Skyler Grey75ea9172022-08-06 10:22:23 +0100303 }
pineafan63fc5e22022-08-04 22:04:10 +0100304 i.deferUpdate();
pineafan3a02ea32022-08-11 21:35:04 +0100305 if (!("customId" in i.component)) {
306 continue;
307 } else if (i.component.customId === "left") {
pineafan813bdf42022-07-24 10:39:10 +0100308 if (page > 0) page--;
309 selectPaneOpen = false;
pineafane23c4ec2022-07-27 21:56:27 +0100310 } else if (i.component.customId === "right") {
pineafan813bdf42022-07-24 10:39:10 +0100311 if (page < pages.length - 1) page++;
312 selectPaneOpen = false;
pineafane23c4ec2022-07-27 21:56:27 +0100313 } else if (i.component.customId === "select") {
pineafan813bdf42022-07-24 10:39:10 +0100314 selectPaneOpen = !selectPaneOpen;
pineafane23c4ec2022-07-27 21:56:27 +0100315 } else if (i.component.customId === "page") {
pineafan3a02ea32022-08-11 21:35:04 +0100316 page = parseInt((i as SelectMenuInteraction).values[0]!);
pineafan813bdf42022-07-24 10:39:10 +0100317 selectPaneOpen = false;
318 } else {
Skyler Greyf21323a2022-08-13 23:58:22 +0100319 cancelled = true;
pineafan813bdf42022-07-24 10:39:10 +0100320 }
321 }
Skyler Greyf21323a2022-08-13 23:58:22 +0100322 if (timedOut) {
323 if (interaction) {
TheCodedProf21c08592022-09-13 14:14:43 -0400324 const em = new Discord.EmbedBuilder(pages[page]!.embed);
Skyler Greyf21323a2022-08-13 23:58:22 +0100325 em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page)).setFooter({
326 text: "Message timed out"
327 });
328 await interaction.editReply({
329 embeds: [em],
330 components: []
331 });
332 } else {
TheCodedProf21c08592022-09-13 14:14:43 -0400333 const em = new Discord.EmbedBuilder(pages[page]!.embed);
Skyler Greyf21323a2022-08-13 23:58:22 +0100334 em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page)).setFooter({
335 text: "Message timed out"
336 });
337 await m.edit({
338 embeds: [em],
339 components: []
340 });
341 }
pineafan813bdf42022-07-24 10:39:10 +0100342 } else {
Skyler Greyf21323a2022-08-13 23:58:22 +0100343 if (interaction) {
TheCodedProf21c08592022-09-13 14:14:43 -0400344 const em = new Discord.EmbedBuilder(pages[page]!.embed);
Skyler Greyf21323a2022-08-13 23:58:22 +0100345 em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page));
346 em.setFooter({ text: "Message closed" });
347 interaction.editReply({
348 embeds: [em],
349 components: []
350 });
351 } else {
352 m.delete();
353 }
pineafan813bdf42022-07-24 10:39:10 +0100354 }
pineafan63fc5e22022-08-04 22:04:10 +0100355};