| import { LoadingEmbed } from "../utils/defaults.js"; |
| import Discord, { |
| SlashCommandBuilder, |
| CommandInteraction, |
| ActionRowBuilder, |
| ButtonBuilder, |
| ButtonStyle, |
| StringSelectMenuOptionBuilder, |
| SelectMenuOptionBuilder, |
| StringSelectMenuBuilder |
| } from "discord.js"; |
| import EmojiEmbed from "../utils/generateEmojiEmbed.js"; |
| import getEmojiByName from "../utils/getEmojiByName.js"; |
| import createPageIndicator from "../utils/createPageIndicator.js"; |
| import client from "../utils/client.js"; |
| import confirmationMessage from "../utils/confirmationMessage.js"; |
| import { Embed } from "../utils/defaults.js"; |
| |
| const command = new SlashCommandBuilder() |
| .setName("privacy") |
| .setDescription("Information and options for you and your server's settings"); |
| |
| const callback = async (interaction: CommandInteraction): Promise<void> => { |
| const pages = [ |
| new Embed() |
| .setEmbed( |
| new EmojiEmbed() |
| .setTitle("Nucleus Privacy") |
| .setDescription( |
| "Nucleus is a bot that naturally needs to store data about servers.\n" + |
| "We are entirely [open source](https://github.com/ClicksMinutePer/Nucleus), so you can check exactly what we store, and how it works.\n\n" + |
| "Any questions about Nucleus, how it works, and what data is stored can be asked in [our server](https://discord.gg/bPaNnxe)." + |
| "\n\n[[Privacy Policy]](https://clicksminuteper.github.io/policies/nucleus) | [[Terms of Service]](https://clicksminuteper.github.io/policies/nucleustos)" |
| ) |
| .setEmoji("NUCLEUS.LOGO") |
| .setStatus("Danger") |
| ) |
| .setTitle("Welcome") |
| .setDescription("General privacy information") |
| .setPageId(0), |
| new Embed() |
| .setEmbed( |
| new EmojiEmbed() |
| .setTitle("Scanners") |
| .setDescription( |
| "Nucleus scans content sent by users for malware and NSFW content\n" + |
| 'Malware is detected using [ClamAV](https://clamav.net/), and the standard ClamAV database."\n' + |
| "NSFW detection is provided by [NsfwJS](https://nsfwjs.com/), with a model provided by [GantMan](https://github.com/GantMan/nsfw_model/releases/tag/1.1.0)\n\n" + |
| "All data is processed on our servers and is not processed by a 3rd party." |
| ) |
| .setEmoji("NUCLEUS.LOGO") |
| .setStatus("Danger") |
| ) |
| .setTitle("Scanners") |
| .setDescription("About Scanners") |
| .setPageId(1), |
| new Embed() |
| .setEmbed( |
| new EmojiEmbed() |
| .setTitle("Link scanning and Transcripts") |
| .setDescription( |
| "Transcripts allow you to store all messages sent in a channel. Transcripts are stored in our database along with the rest of the server's settings but is accessible by anyone with the link, so a leaked link could show all messages sent in the channel.\n" |
| ) |
| .setEmoji("NUCLEUS.LOGO") |
| .setStatus("Danger") |
| ) |
| .setTitle("Link scanning and Transcripts") |
| .setDescription("Information about how links and images are scanned, and transcripts are stored") |
| .setPageId(2) |
| ].concat( |
| (interaction.member as Discord.GuildMember).id === interaction.guild!.ownerId |
| ? [ |
| new Embed() |
| .setEmbed( |
| new EmojiEmbed() |
| .setTitle("Options") |
| .setDescription("Below are buttons for controlling this servers privacy settings") |
| .setEmoji("NUCLEUS.LOGO") |
| .setStatus("Danger") |
| ) |
| .setTitle("Options") |
| .setDescription("Options") |
| .setPageId(3) |
| .setComponents([ |
| new ActionRowBuilder<ButtonBuilder>().addComponents([ |
| new ButtonBuilder() |
| .setLabel("Clear all data") |
| .setCustomId("clear-all-data") |
| .setStyle(ButtonStyle.Danger) |
| ]) |
| ]) |
| ] |
| : [] |
| ); |
| const m = await interaction.reply({ |
| embeds: LoadingEmbed, |
| fetchReply: true, |
| ephemeral: true |
| }); |
| let page = parseInt( |
| client.preloadPage[interaction.channel!.id] ? client.preloadPage[interaction.channel!.id]?.argument! : "0" |
| ); |
| |
| let selectPaneOpen = false; |
| let nextFooter = null; |
| |
| let timedOut = false; |
| while (!timedOut) { |
| let selectPane: Discord.ActionRowBuilder<ButtonBuilder | StringSelectMenuBuilder>[] = []; |
| |
| if (selectPaneOpen) { |
| const options: SelectMenuOptionBuilder[] = []; |
| pages.forEach((embed) => { |
| options.push( |
| new StringSelectMenuOptionBuilder({ |
| label: embed.title, |
| value: embed.pageId.toString(), |
| description: embed.description || "" |
| }) |
| ); |
| }); |
| selectPane = [ |
| new ActionRowBuilder<StringSelectMenuBuilder>().addComponents([ |
| new Discord.StringSelectMenuBuilder() |
| .addOptions(options) |
| .setCustomId("page") |
| .setMaxValues(1) |
| .setPlaceholder("Choose a page...") |
| ]) |
| ]; |
| } |
| const components: ActionRowBuilder<ButtonBuilder | StringSelectMenuBuilder>[] = selectPane.concat([ |
| new ActionRowBuilder<ButtonBuilder>().addComponents( |
| new ButtonBuilder() |
| .setCustomId("left") |
| .setEmoji(getEmojiByName("CONTROL.LEFT", "id")) |
| .setStyle(ButtonStyle.Secondary) |
| .setDisabled(page === 0), |
| new ButtonBuilder() |
| .setCustomId("select") |
| .setEmoji(getEmojiByName("CONTROL.MENU", "id")) |
| .setStyle(selectPaneOpen ? ButtonStyle.Primary : ButtonStyle.Secondary) |
| .setDisabled(false), |
| new ButtonBuilder() |
| .setCustomId("right") |
| .setEmoji(getEmojiByName("CONTROL.RIGHT", "id")) |
| .setStyle(ButtonStyle.Secondary) |
| .setDisabled(page === pages.length - 1) |
| ) |
| ]); |
| const em = new Discord.EmbedBuilder(pages[page]!.embed); |
| em.setDescription(em.data.description + "\n\n" + createPageIndicator(pages.length, page)); |
| if (nextFooter) em.setFooter({ text: nextFooter }); |
| await interaction.editReply({ |
| embeds: [em], |
| components: components.concat(pages[page]?.componentsToSet ?? []) |
| }); |
| let i; |
| try { |
| i = await m.awaitMessageComponent({ |
| time: 300000, |
| filter: (i) => { |
| return ( |
| i.user.id === interaction.user.id && |
| i.channel!.id === interaction.channel!.id && |
| i.message.id === m.id |
| ); |
| } |
| }); |
| } catch (e) { |
| timedOut = true; |
| continue; |
| } |
| nextFooter = null; |
| await i.deferUpdate(); |
| if (i.customId === "left") { |
| if (page > 0) page--; |
| selectPaneOpen = false; |
| } else if (i.customId === "right") { |
| if (page < pages.length - 1) page++; |
| selectPaneOpen = false; |
| } else if (i.customId === "select") { |
| selectPaneOpen = !selectPaneOpen; |
| } else if (i.customId === "page" && i.isStringSelectMenu()) { |
| page = parseInt(i.values[0]!); |
| selectPaneOpen = false; |
| } else if (i.customId === "clear-all-data") { |
| const confirmation = await new confirmationMessage(interaction) |
| .setEmoji("CONTROL.BLOCKCROSS") |
| .setTitle("Clear All Data") |
| .setDescription( |
| "Are you sure you want to delete all data on this server? This includes your settings and all punishment histories.\n\n" + |
| "**This cannot be undone.**" |
| ) |
| .setColor("Danger") |
| .send(true); |
| if (confirmation.cancelled) { |
| continue; |
| } |
| if (confirmation.success) { |
| await client.database.guilds.delete(interaction.guild!.id); |
| await client.database.history.delete(interaction.guild!.id); |
| await client.database.notes.delete(interaction.guild!.id); |
| await client.database.transcripts.deleteAll(interaction.guild!.id); |
| nextFooter = "All data cleared"; |
| continue; |
| } else { |
| nextFooter = "No changes were made"; |
| continue; |
| } |
| } else { |
| const em = new Discord.EmbedBuilder(pages[page]!.embed); |
| em.setDescription(em.data.description + "\n\n" + createPageIndicator(pages.length, page)); |
| em.setFooter({ text: "Message closed" }); |
| await interaction.editReply({ embeds: [em], components: [] }); |
| return; |
| } |
| } |
| const em = new Discord.EmbedBuilder(pages[page]!.embed); |
| em.setDescription(em.data.description + "\n\n" + createPageIndicator(pages.length, page)); |
| em.setFooter({ text: "Message timed out" }); |
| await interaction.editReply({ |
| embeds: [em], |
| components: [] |
| }); |
| }; |
| |
| export { command }; |
| export { callback }; |