fixed settings commands, todo's

pull/11/head
TheCodedProf 3 years ago
parent 362c51d71e
commit 1f67504a24

@ -16,7 +16,7 @@ import addPlural from "../utils/plurals.js";
import client from "../utils/client.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder // TODO: DON'T RELEASE THIS
builder
.setName("all")
.setDescription("Gives or removes a role from everyone");

@ -257,7 +257,7 @@ export default async function (interaction: CommandInteraction | ButtonInteracti
type: Discord.ChannelType.PrivateThread,
reason: "Creating ticket"
}) as Discord.PrivateThreadChannel;
c.members.add(interaction.member!.user.id); // TODO: When a thread is used, and a support role is added, automatically set channel permissions
c.members.add(interaction.member!.user.id);
try {
await c.send({
content:

@ -84,7 +84,12 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
if(currentPath[0] === "" || currentPath[0] === "help") {
embed.setDescription(
`Welcome to Nucleus\n\n` +
`Select a command to get started${(interaction.member?.permissions as PermissionsBitField).has("ManageGuild") ? `, or run ${getCommandMentionByName("nucleus/guide")} for commands to set up your server` : ``}` // FIXME
`Select a command to get started${
(interaction.member?.permissions as PermissionsBitField).has("ManageGuild") ?
`, or run ${getCommandMentionByName("nucleus/guide")} for commands to set up your server` : ``
}\n\n\n` +
`Nucleus is fully [open source](https://github.com/clicksminuteper/Nucleus), and all currently free features will remain free forever.\n\n` +
`You can invite Nucleus to your server using ${getCommandMentionByName("nucleus/invite")}`
)
} else {
const currentData = getCommandByName(currentPath.filter(value => value !== "" && value !== "none").join('/'));
@ -95,7 +100,7 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
nameLocalized?: string;
descriptionLocalized?: string;
})[] = [];
//options
//o ptions
if(currentPath[1] !== "" && currentPath[1] !== "none" && currentPath[2] !== "" && currentPath[2] !== "none") {
const Op = current.options.find(option => option.name === currentPath[1])! as ApplicationCommandSubGroup
const Op2 = Op.options!.find(option => option.name === currentPath[2])!

@ -103,7 +103,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
let component;
try {
component = await m.awaitMessageComponent({
filter: (i) => {return i.user.id === interaction.user.id && i.id === m.id},
filter: (i) => {return i.user.id === interaction.user.id && i.channelId === interaction.channelId},
time: 300000
});
} catch {

@ -47,7 +47,7 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
}) + "Are you sure you want to set the slowmode in this channel?"
)
.setColor("Danger")
.setFailedMessage("No changes were made", "Danger", "CHANNEL.SLOWMODE.ON")
.setFailedMessage("No changes were made", "Success", "CHANNEL.SLOWMODE.ON")
.send();
if (confirmation.cancelled || !confirmation.success) return;
try {

@ -21,7 +21,6 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
.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" +
"If you are a server administrator, you can view the options page in the dropdown under this message.\n\n" + // TODO
"Any questions about Nucleus, how it works, and what data is stored can be asked in [our server](https://discord.gg/bPaNnxe)."
)
.setEmoji("NUCLEUS.LOGO")
@ -51,7 +50,7 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
.setDescription(
"**Facebook** - Facebook trackers include data such as your date of birth, and guess your age if not entered, your preferences, who you interact with and more.\n" +
"**AMP** - AMP is a technology that allows websites to be served by Google. This means Google can store and track data, and are pushing this to as many pages as possible.\n\n" +
"Transcripts allow you to store all messages sent in a channel. This could be an issue in some cases, as they are hosted on [Pastebin](https://pastebin.com), so a leaked link could show all messages sent in the channel.\n" // TODO: Not on pastebin
"Transcripts allow you to store all messages sent in a channel. This is stored in our database along with the rest of the servers 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")
@ -62,26 +61,26 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
].concat(
(interaction.member as Discord.GuildMember).permissions.has("Administrator")
? [
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)
])
])
]
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({

@ -29,6 +29,7 @@ import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import client from "../../utils/client.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import { modalInteractionCollector } from "../../utils/dualCollector.js";
import listToAndMore from "../../utils/listToAndMore.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
@ -37,14 +38,6 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
const emojiFromBoolean = (bool: boolean, id?: string) => bool ? getEmojiByName("CONTROL.TICK", id) : getEmojiByName("CONTROL.CROSS", id);
const listToAndMore = (list: string[], max: number) => {
// PineappleFan, Coded, Mini (and 10 more)
if(list.length > max) {
return list.slice(0, max).join(", ") + ` (and ${list.length - max} more)`;
}
return list.join(", ");
}
const toSelectMenu = async (interaction: StringSelectMenuInteraction, m: Message, ids: string[], type: "member" | "role" | "channel", title: string): Promise<string[]> => {
const back = new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder().setCustomId("back").setLabel("Back").setStyle(ButtonStyle.Secondary).setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji));

@ -31,7 +31,7 @@ const logs: Record<string, string> = {
const command = (builder: SlashCommandSubcommandBuilder) =>
builder.setName("events").setDescription("Sets what events should be logged");
const callback = async (interaction: CommandInteraction): Promise<void> => { // TODO: Maybe merge this into /settings log general
const callback = async (interaction: CommandInteraction): Promise<void> => {
await interaction.reply({
embeds: LoadingEmbed,
fetchReply: true,

@ -1,45 +1,23 @@
import { LoadingEmbed } from "../../utils/defaults.js";
import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, Role, ButtonStyle, ButtonComponent, TextInputBuilder, Message } from "discord.js";
import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, ButtonComponent, TextInputBuilder, Message, RoleSelectMenuBuilder } from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import type { SlashCommandSubcommandBuilder } from "discord.js";
import client from "../../utils/client.js";
import { modalInteractionCollector } from "../../utils/dualCollector.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
import keyValueList from "../../utils/generateKeyValueList.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder
.setName("commands")
.setName("moderation")
.setDescription("Links and text shown to a user after a moderator action is performed")
.addRoleOption((o) => o.setName("role").setDescription("The role given when a member is muted"));
const callback = async (interaction: CommandInteraction): Promise<unknown> => {
const callback = async (interaction: CommandInteraction): Promise<void> => {
await interaction.reply({
embeds: LoadingEmbed,
ephemeral: true,
fetchReply: true
});
let m: Message;
let clicked = "";
if (interaction.options.get("role")) {
const confirmation = await new confirmationMessage(interaction)
.setEmoji("GUILD.ROLES.DELETE")
.setTitle("Moderation Commands")
.setDescription(
keyValueList({
role: `<@&${(interaction.options.get("role") as unknown as Role).id}>`
})
)
.setColor("Danger")
.send(true);
if (confirmation.cancelled) return
if (confirmation.success) {
await client.database.guilds.write(interaction.guild!.id, {
["moderation.mute.role"]: (interaction.options.get("role") as unknown as Role).id
});
}
}
let timedOut = false;
while (!timedOut) {
const config = await client.database.guilds.read(interaction.guild!.id);
@ -52,8 +30,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setStatus("Success")
.setDescription(
"These links are shown below the message sent in a user's DM when they are punished.\n\n" +
"**Mute Role:** " +
(moderation.mute.role ? `<@&${moderation.mute.role}>` : "*None set*")
"**Mute Role:** " + (moderation.mute.role ? `<@&${moderation.mute.role}>` : "*None set*")
)
],
components: [
@ -92,18 +69,17 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setStyle(ButtonStyle.Secondary)
]),
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setLabel(clicked === "clearMuteRole" ? "Click again to confirm" : "Clear mute role")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
.setCustomId("clearMuteRole")
.setStyle(ButtonStyle.Danger)
.setDisabled(!moderation.mute.role),
new ButtonBuilder()
.setCustomId("timeout")
.setLabel("Mute timeout " + (moderation.mute.timeout ? "Enabled" : "Disabled"))
.setStyle(moderation.mute.timeout ? ButtonStyle.Success : ButtonStyle.Danger)
.setEmoji(getEmojiByName("CONTROL." + (moderation.mute.timeout ? "TICK" : "CROSS"), "id"))
])
]),
new ActionRowBuilder<RoleSelectMenuBuilder>().addComponents(
new RoleSelectMenuBuilder()
.setCustomId("muteRole")
.setPlaceholder("Select a new mute role")
)
]
});
let i;
@ -118,20 +94,13 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
}
type modIDs = "mute" | "kick" | "ban" | "softban" | "warn" | "role";
let chosen = moderation[i.customId as modIDs];
if ((i.component as ButtonComponent).customId === "clearMuteRole") {
if (i.isRoleSelectMenu()) {
await i.deferUpdate();
if (clicked === "clearMuteRole") {
await client.database.guilds.write(interaction.guild!.id, {
"moderation.mute.role": null
});
} else {
clicked = "clearMuteRole";
}
await client.database.guilds.write(interaction.guild!.id, {
"moderation.mute.role": i.values[0]!
});
continue;
} else {
clicked = "";
}
if ((i.component as ButtonComponent).customId === "timeout") {
} else if ((i.component as ButtonComponent).customId === "timeout") {
await i.deferUpdate();
await client.database.guilds.write(interaction.guild!.id, {
"moderation.mute.timeout": !moderation.mute.timeout

@ -107,7 +107,7 @@ const editNameDescription = async (i: ButtonInteraction, interaction: StringSele
new TextInputBuilder()
.setLabel("Name")
.setCustomId("name")
.setPlaceholder("Name here...") // TODO: Make better placeholder
.setPlaceholder("The name of the role (e.g. Programmer)")
.setStyle(TextInputStyle.Short)
.setValue(name ?? "")
.setRequired(true)
@ -117,7 +117,7 @@ const editNameDescription = async (i: ButtonInteraction, interaction: StringSele
new TextInputBuilder()
.setLabel("Description")
.setCustomId("description")
.setPlaceholder("Description here...") // TODO: Make better placeholder
.setPlaceholder("A short description of the role (e.g. A role for people who code)")
.setStyle(TextInputStyle.Short)
.setValue(description ?? "")
)

@ -1,66 +1,38 @@
import { LoadingEmbed } from "../../utils/defaults.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
import Discord, {
CommandInteraction,
GuildChannel,
Message,
ActionRowBuilder,
ButtonBuilder,
MessageComponentInteraction,
StringSelectMenuBuilder,
Role,
ButtonStyle,
TextInputBuilder,
ButtonComponent,
ModalSubmitInteraction,
APIMessageComponentEmoji
APIMessageComponentEmoji,
RoleSelectMenuBuilder,
ChannelSelectMenuBuilder,
RoleSelectMenuInteraction,
ButtonInteraction,
ChannelSelectMenuInteraction,
TextInputStyle,
ModalBuilder,
ChannelType
} from "discord.js";
import { SlashCommandSubcommandBuilder, StringSelectMenuOptionBuilder } from "discord.js";
import { ChannelType } from "discord-api-types/v9";
import client from "../../utils/client.js";
import { toHexInteger, toHexArray, tickets as ticketTypes } from "../../utils/calculate.js";
import { capitalize } from "../../utils/generateKeyValueList.js";
import { modalInteractionCollector } from "../../utils/dualCollector.js";
import type { GuildConfig } from "../../utils/database.js";
import { LinkWarningFooter } from "../../utils/defaults.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder
.setName("tickets")
.setDescription("Shows settings for tickets | Use no arguments to manage custom types")
.addStringOption((option) =>
option
.setName("enabled")
.setDescription("If users should be able to create tickets")
.setRequired(false)
.addChoices(
{name: "Yes", value: "yes"},
{name: "No",value: "no"}
)
)
.addChannelOption((option) =>
option
.setName("category")
.setDescription("The category where tickets are created")
.addChannelTypes(ChannelType.GuildCategory)
.setRequired(false)
)
.addNumberOption((option) =>
option
.setName("maxticketsperuser")
.setDescription("The maximum amount of tickets a user can create | Default: 5")
.setRequired(false)
.setMinValue(1)
)
.addRoleOption((option) =>
option
.setName("supportrole")
.setDescription(
"This role will have view access to all tickets and will be pinged when a ticket is created"
)
.setRequired(false)
);
.setDescription("Shows settings for tickets")
const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (!interaction.guild) return;
@ -69,215 +41,80 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
ephemeral: true,
fetchReply: true
})) as Message;
const options = {
enabled: (interaction.options.get("enabled")?.value as string).startsWith("yes") as boolean | null,
category: interaction.options.get("category")?.channel as Discord.CategoryChannel | null,
maxtickets: interaction.options.get("maxticketsperuser")?.value as number | null,
supportping: interaction.options.get("supportrole")?.role as Role | null
};
if (options.enabled !== null || options.category || options.maxtickets || options.supportping) {
if (options.category) {
let channel: GuildChannel | null;
try {
channel = await interaction.guild.channels.fetch(options.category.id) as GuildChannel;
} catch {
return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("CHANNEL.TEXT.DELETE")
.setTitle("Tickets > Category")
.setDescription("The channel you provided is not a valid category")
.setStatus("Danger")
]
});
}
channel = channel as Discord.CategoryChannel;
if (channel.guild.id !== interaction.guild.id)
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Tickets > Category")
.setDescription("You must choose a category in this server")
.setStatus("Danger")
.setEmoji("CHANNEL.TEXT.DELETE")
]
});
}
if (options.maxtickets) {
if (options.maxtickets < 1)
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Tickets > Max Tickets")
.setDescription("You must choose a number greater than 0")
.setStatus("Danger")
.setEmoji("CHANNEL.TEXT.DELETE")
]
});
}
let role: Role | null;
if (options.supportping) {
try {
role = await interaction.guild.roles.fetch(options.supportping.id);
} catch {
return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("GUILD.ROLE.DELETE")
.setTitle("Tickets > Support Ping")
.setDescription("The role you provided is not a valid role")
.setStatus("Danger")
]
});
}
if (!role) return;
role = role as Discord.Role;
if (role.guild.id !== interaction.guild.id)
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Tickets > Support Ping")
.setDescription("You must choose a role in this server")
.setStatus("Danger")
.setEmoji("GUILD.ROLE.DELETE")
]
});
}
const confirmation = await new confirmationMessage(interaction)
.setEmoji("GUILD.TICKET.ARCHIVED")
.setTitle("Tickets")
.setDescription(
(options.category ? `**Category:** ${options.category.name}\n` : "") +
(options.maxtickets ? `**Max Tickets:** ${options.maxtickets}\n` : "") +
(options.supportping ? `**Support Ping:** ${options.supportping.name}\n` : "") +
(options.enabled !== null
? `**Enabled:** ${
options.enabled
? `${getEmojiByName("CONTROL.TICK")} Yes`
: `${getEmojiByName("CONTROL.CROSS")} No`
}\n`
: "") +
"\nAre you sure you want to apply these settings?"
)
.setColor("Warning")
.setFailedMessage("No changes were made", "Success", "GUILD.TICKET.OPEN")
.setInverted(true)
.send(true);
if (confirmation.cancelled) return;
if (confirmation.success) {
const toUpdate: Record<string, string | boolean | number> = {};
if (options.enabled !== null) toUpdate["tickets.enabled"] = options.enabled;
if (options.category) toUpdate["tickets.category"] = options.category.id;
if (options.maxtickets) toUpdate["tickets.maxTickets"] = options.maxtickets;
if (options.supportping) toUpdate["tickets.supportRole"] = options.supportping.id;
try {
await client.database.guilds.write(interaction.guild.id, toUpdate);
} catch (e) {
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Tickets")
.setDescription("Something went wrong and the staff notifications channel could not be set")
.setStatus("Danger")
.setEmoji("GUILD.TICKET.DELETE")
],
components: []
});
}
} else {
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Tickets")
.setDescription("No changes were made")
.setStatus("Success")
.setEmoji("GUILD.TICKET.OPEN")
],
components: []
});
}
}
const data = await client.database.guilds.read(interaction.guild.id);
data.tickets.customTypes = (data.tickets.customTypes ?? []).filter(
(value: string, index: number, array: string[]) => array.indexOf(value) === index
);
let lastClicked = "";
const embed: EmojiEmbed = new EmojiEmbed();
const compiledData = {
enabled: data.tickets.enabled,
category: data.tickets.category,
maxTickets: data.tickets.maxTickets,
supportRole: data.tickets.supportRole,
useCustom: data.tickets.useCustom,
types: data.tickets.types,
customTypes: data.tickets.customTypes as string[] | null
};
let ticketData = (await client.database.guilds.read(interaction.guild.id)).tickets
let changesMade = false;
let timedOut = false;
let errorMessage = "";
while (!timedOut) {
embed
const embed: EmojiEmbed = new EmojiEmbed()
.setTitle("Tickets")
.setDescription(
`${compiledData.enabled ? "" : getEmojiByName("TICKETS.REPORT")} **Enabled:** ${
compiledData.enabled ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`
`${ticketData.enabled ? "" : getEmojiByName("TICKETS.REPORT")} **Enabled:** ${
ticketData.enabled ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`
}\n` +
`${compiledData.category ? "" : getEmojiByName("TICKETS.REPORT")} **Category:** ${
compiledData.category ? `<#${compiledData.category}>` : "*None set*"
}\n` +
`**Max Tickets:** ${compiledData.maxTickets ? compiledData.maxTickets : "*No limit*"}\n` +
`**Support Ping:** ${compiledData.supportRole ? `<@&${compiledData.supportRole}>` : "*None set*"}\n\n` +
(compiledData.useCustom && compiledData.customTypes === null ? `${getEmojiByName("TICKETS.REPORT")} ` : "") +
`${compiledData.useCustom ? "Custom" : "Default"} types in use` +
`${ticketData.category ? "" : getEmojiByName("TICKETS.REPORT")}` +
((await interaction.guild.channels.fetch(ticketData.category!))!.type === ChannelType.GuildCategory ?
`**Category:** ` : `**Channel:** `) + // TODO: Notify if permissions are wrong
`${ticketData.category ? `<#${ticketData.category}>` : "*None set*"}\n` +
`**Max Tickets:** ${ticketData.maxTickets ? ticketData.maxTickets : "*No limit*"}\n` +
`**Support Ping:** ${ticketData.supportRole ? `<@&${ticketData.supportRole}>` : "*None set*"}\n\n` +
(ticketData.useCustom && ticketData.customTypes === null ? `${getEmojiByName("TICKETS.REPORT")} ` : "") +
`${ticketData.useCustom ? "Custom" : "Default"} types in use` +
"\n\n" +
`${getEmojiByName("TICKETS.REPORT")} *Indicates a setting stopping tickets from being used*`
)
.setStatus("Success")
.setEmoji("GUILD.TICKET.OPEN");
if (errorMessage) embed.setFooter({text: errorMessage, iconURL: LinkWarningFooter.iconURL});
m = (await interaction.editReply({
embeds: [embed],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("Tickets " + (compiledData.enabled ? "enabled" : "disabled"))
.setEmoji(getEmojiByName("CONTROL." + (compiledData.enabled ? "TICK" : "CROSS"), "id"))
.setStyle(compiledData.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
.setLabel("Tickets " + (ticketData.enabled ? "enabled" : "disabled"))
.setEmoji(getEmojiByName("CONTROL." + (ticketData.enabled ? "TICK" : "CROSS"), "id"))
.setStyle(ticketData.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
.setCustomId("enabled"),
new ButtonBuilder()
.setLabel(lastClicked === "cat" ? "Click again to confirm" : "Clear category")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
.setStyle(ButtonStyle.Danger)
.setCustomId("clearCategory")
.setDisabled(compiledData.category === null),
new ButtonBuilder()
.setLabel(lastClicked === "max" ? "Click again to confirm" : "Reset max tickets")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
.setStyle(ButtonStyle.Danger)
.setCustomId("clearMaxTickets")
.setDisabled(compiledData.maxTickets === 5),
new ButtonBuilder()
.setLabel(lastClicked === "sup" ? "Click again to confirm" : "Clear support ping")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
.setStyle(ButtonStyle.Danger)
.setCustomId("clearSupportPing")
.setDisabled(compiledData.supportRole === null)
]),
new ActionRowBuilder<ButtonBuilder>().addComponents([
.setLabel("Set max tickets")
.setEmoji(getEmojiByName("CONTROL.TICKET", "id"))
.setStyle(ButtonStyle.Primary)
.setCustomId("setMaxTickets")
.setDisabled(!ticketData.enabled),
new ButtonBuilder()
.setLabel("Manage types")
.setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
.setStyle(ButtonStyle.Secondary)
.setCustomId("manageTypes"),
.setCustomId("manageTypes")
.setDisabled(!ticketData.enabled),
new ButtonBuilder()
.setLabel("Add create ticket button")
.setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
.setStyle(ButtonStyle.Primary)
.setCustomId("send")
])
.setLabel("Save")
.setEmoji(getEmojiByName("ICONS.SAVE", "id"))
.setStyle(ButtonStyle.Success)
.setCustomId("save")
.setDisabled(!changesMade)
),
new ActionRowBuilder<RoleSelectMenuBuilder>().addComponents(
new RoleSelectMenuBuilder()
.setCustomId("supportRole")
.setPlaceholder("Select a support role")
.setDisabled(!ticketData.enabled)
),
new ActionRowBuilder<ChannelSelectMenuBuilder>().addComponents(
new ChannelSelectMenuBuilder()
.setCustomId("category")
.setPlaceholder("Select a category or channel")
.setDisabled(!ticketData.enabled)
)
]
})) as Message;
let i: MessageComponentInteraction;
}));
let i: RoleSelectMenuInteraction | ButtonInteraction | ChannelSelectMenuInteraction;
try {
i = await m.awaitMessageComponent({
i = await m.awaitMessageComponent<2 | 6 | 8>({
time: 300000,
filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id && i.message.id === m.id }
});
@ -285,176 +122,49 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
timedOut = true;
continue;
}
await i.deferUpdate();
if ((i.component as ButtonComponent).customId === "clearCategory") {
if (lastClicked === "cat") {
lastClicked = "";
await client.database.guilds.write(interaction.guild.id, null, ["tickets.category"]);
compiledData.category = null;
} else lastClicked = "cat";
} else if ((i.component as ButtonComponent).customId === "clearMaxTickets") {
if (lastClicked === "max") {
lastClicked = "";
await client.database.guilds.write(interaction.guild.id, null, ["tickets.maxTickets"]);
compiledData.maxTickets = 5;
} else lastClicked = "max";
} else if ((i.component as ButtonComponent).customId === "clearSupportPing") {
if (lastClicked === "sup") {
lastClicked = "";
await client.database.guilds.write(interaction.guild.id, null, ["tickets.supportRole"]);
compiledData.supportRole = null;
} else lastClicked = "sup";
} else if ((i.component as ButtonComponent).customId === "send") {
const ticketMessages = [
{
label: "Create ticket",
description: "Click the button below to create a ticket"
},
{
label: "Issues, questions or feedback?",
description: "Click below to open a ticket and get help from our staff team"
},
{
label: "Contact Us",
description: "Click the button below to speak to us privately"
}
];
let innerTimedOut = false;
let templateSelected = false;
while (!innerTimedOut && !templateSelected) {
const enabled = compiledData.enabled && compiledData.category !== null;
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Ticket Button")
.setDescription("Select a message template to send in this channel")
.setFooter({
text: enabled
? ""
: "Tickets are not set up correctly so the button may not work for users. Check the main menu to find which options must be set."
})
.setStatus(enabled ? "Success" : "Warning")
.setEmoji("GUILD.ROLES.CREATE")
],
components: [
new ActionRowBuilder<StringSelectMenuBuilder>().addComponents([
new StringSelectMenuBuilder()
.setOptions(
ticketMessages.map(
(
t: {
label: string;
description: string;
value?: string;
},
index
) => {
t.value = index.toString();
return t as {
value: string;
label: string;
description: string;
};
}
)
)
.setCustomId("template")
.setMaxValues(1)
.setMinValues(1)
.setPlaceholder("Select a message template")
]),
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setCustomId("back")
.setLabel("Back")
.setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
.setStyle(ButtonStyle.Danger),
new ButtonBuilder().setCustomId("blank").setLabel("Empty").setStyle(ButtonStyle.Secondary),
new ButtonBuilder()
.setCustomId("custom")
.setLabel("Custom")
.setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
.setStyle(ButtonStyle.Primary)
])
]
});
let i: MessageComponentInteraction;
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) {
innerTimedOut = true;
continue;
}
if (i.isStringSelectMenu() && i.customId === "template") {
changesMade = true;
if (i.isRoleSelectMenu()) {
await i.deferUpdate();
ticketData.supportRole = i.values[0] ?? null;
} else if (i.isChannelSelectMenu()) {
await i.deferUpdate();
ticketData.category = i.values[0] ?? null;
} else {
switch(i.customId) {
case "save": {
await i.deferUpdate();
await interaction.channel!.send({
embeds: [
new EmojiEmbed()
.setTitle(ticketMessages[parseInt(i.values[0]!)]!.label)
.setDescription(
ticketMessages[parseInt(i.values[0]!)]!.description
)
.setStatus("Success")
.setEmoji("GUILD.TICKET.OPEN")
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setLabel("Create Ticket")
.setEmoji(getEmojiByName("CONTROL.TICK", "id"))
.setStyle(ButtonStyle.Success)
.setCustomId("createticket")
])
]
});
templateSelected = true;
continue;
} else if ((i.component as ButtonComponent).customId === "blank") {
await client.database.guilds.write(interaction.guild.id, { tickets: ticketData });
changesMade = false;
break;
}
case "enabled": {
await i.deferUpdate();
await interaction.channel!.send({
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setLabel("Create Ticket")
.setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
.setStyle(ButtonStyle.Success)
.setCustomId("createticket")
])
]
});
templateSelected = true;
continue;
} else if ((i.component as ButtonComponent).customId === "custom") {
ticketData.enabled = !ticketData.enabled;
break;
}
case "setMaxTickets": {
await i.showModal(
new Discord.ModalBuilder()
.setCustomId("modal")
.setTitle("Enter embed details")
new ModalBuilder()
.setCustomId("maxTickets")
.setTitle("Set max tickets")
.addComponents(
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("title")
.setLabel("Title")
.setMaxLength(256)
.setRequired(true)
.setStyle(Discord.TextInputStyle.Short)
),
new ActionRowBuilder<TextInputBuilder>().addComponents(
new ActionRowBuilder<TextInputBuilder>().setComponents(
new TextInputBuilder()
.setCustomId("description")
.setLabel("Description")
.setMaxLength(4000)
.setRequired(true)
.setStyle(Discord.TextInputStyle.Paragraph)
.setLabel("Max tickets - Leave blank for no limit")
.setCustomId("maxTickets")
.setPlaceholder("Enter a number")
.setRequired(false)
.setValue(ticketData.maxTickets.toString() ?? "")
.setMinLength(1)
.setMaxLength(3)
.setStyle(TextInputStyle.Short)
)
)
);
await interaction.editReply({
)
await i.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Ticket Button")
.setTitle("Tickets")
.setDescription("Modal opened. If you can't see it, click back and try again.")
.setStatus("Success")
.setEmoji("GUILD.TICKET.OPEN")
@ -473,52 +183,34 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
try {
out = await modalInteractionCollector(
m,
(m) => m.channel!.id === interaction.channel!.id,
(m) => m.customId === "modify"
(m) => m.user.id === interaction.user.id,
(m) => m.customId === "back"
);
} catch (e) {
innerTimedOut = true;
continue;
}
if (!out || out.isButton()) continue;
out = out as ModalSubmitInteraction;
const title = out.fields.getTextInputValue("title");
const description = out.fields.getTextInputValue("description");
await interaction.channel!.send({
embeds: [
new EmojiEmbed()
.setTitle(title)
.setDescription(description)
.setStatus("Success")
.setEmoji("GUILD.TICKET.OPEN")
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setLabel("Create Ticket")
.setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
.setStyle(ButtonStyle.Success)
.setCustomId("createticket")
])
]
});
templateSelected = true;
let toAdd = out.fields.getTextInputValue("maxTickets");
if(isNaN(parseInt(toAdd))) {
errorMessage = "You entered an invalid number - No changes were made";
break;
}
ticketData.maxTickets = toAdd === "" ? 0 : parseInt(toAdd);
break;
}
case "manageTypes": {
await i.deferUpdate();
ticketData = await manageTypes(interaction, data.tickets, m);
break;
}
}
} else if ((i.component as ButtonComponent).customId === "enabled") {
await client.database.guilds.write(interaction.guild.id, {
"tickets.enabled": !compiledData.enabled
});
compiledData.enabled = !compiledData.enabled;
} else if ((i.component as ButtonComponent).customId === "manageTypes") {
data.tickets = await manageTypes(interaction, data.tickets, m as Message);
}
}
await interaction.editReply({
embeds: [ embed.setFooter({ text: "Message timed out" })],
components: []
});
};
async function manageTypes(interaction: CommandInteraction, data: GuildConfig["tickets"], m: Message) {
let timedOut = false;
let backPressed = false;
@ -543,7 +235,7 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t
.setStatus("Success")
.setEmoji("GUILD.TICKET.OPEN")
],
components: (customTypes
components: (customTypes && customTypes.length > 0
? [
new ActionRowBuilder<StringSelectMenuBuilder | ButtonBuilder>().addComponents([
new Discord.StringSelectMenuBuilder()
@ -644,9 +336,6 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t
if (i.isStringSelectMenu() && i.customId === "types") {
await i.deferUpdate();
const types = toHexInteger(i.values, ticketTypes);
await client.database.guilds.write(interaction.guild!.id, {
"tickets.types": types
});
data.types = types;
} else if (i.isStringSelectMenu() && i.customId === "removeTypes") {
await i.deferUpdate();
@ -655,9 +344,6 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t
if (customTypes) {
customTypes = customTypes.filter((t) => !types.includes(t));
customTypes = customTypes.length > 0 ? customTypes : null;
await client.database.guilds.write(interaction.guild!.id, {
"tickets.customTypes": customTypes
});
data.customTypes = customTypes;
}
} else if ((i.component as ButtonComponent).customId === "addType") {
@ -678,7 +364,7 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t
)
)
);
await interaction.editReply({
await i.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Tickets > Types")
@ -700,8 +386,8 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t
try {
out = await modalInteractionCollector(
m,
(m) => m.channel!.id === interaction.channel!.id,
(m) => m.customId === "addType"
(m) => m.user.id === interaction.user.id,
(m) => m.customId === "back"
);
} catch (e) {
continue;
@ -714,7 +400,8 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t
}
toAdd = toAdd.substring(0, 80);
try {
await client.database.guilds.append(interaction.guild!.id, "tickets.customTypes", toAdd);
if(!data.customTypes) data.customTypes = [];
data.customTypes?.push(toAdd);
} catch {
continue;
}

@ -35,7 +35,7 @@ const editName = async (i: ButtonInteraction, interaction: StringSelectMenuInter
new TextInputBuilder()
.setLabel("Name")
.setCustomId("name")
.setPlaceholder("Name here...") // TODO: Make better placeholder
.setPlaceholder("The name of the track (e.g. Moderators)")
.setStyle(TextInputStyle.Short)
.setValue(name)
.setRequired(true)

@ -174,211 +174,11 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
await client.database.guilds.write(interaction.guild.id, null, ["verify.role", "verify.enabled"]);
role = null;
}
} else if ((i.component as ButtonComponent).customId === "send") {
const verifyMessages = [
{
label: "Verify",
description: "Click the button below to get verified"
},
{
label: "Get verified",
description: "To get access to the rest of the server, click the button below"
},
{
label: "Ready to verify?",
description: "Click the button below to verify yourself"
}
];
let innerTimedOut = false;
let templateSelected = false;
while (!innerTimedOut && !templateSelected) {
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Verify Button")
.setDescription("Select a message template to send in this channel")
.setFooter({
text: role ? "" : "You do no have a verify role set so the button will not work."
})
.setStatus(role ? "Success" : "Warning")
.setEmoji("GUILD.ROLES.CREATE")
],
components: [
new ActionRowBuilder<StringSelectMenuBuilder>().addComponents([
new StringSelectMenuBuilder()
.setOptions(
verifyMessages.map(
(
t: {
label: string;
description: string;
value?: string;
},
index
) => {
t.value = index.toString();
return t as {
value: string;
label: string;
description: string;
};
}
)
)
.setCustomId("template")
.setMaxValues(1)
.setMinValues(1)
.setPlaceholder("Select a message template")
]),
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setCustomId("back")
.setLabel("Back")
.setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
.setStyle(ButtonStyle.Danger),
new ButtonBuilder().setCustomId("blank").setLabel("Empty").setStyle(ButtonStyle.Secondary),
new ButtonBuilder()
.setCustomId("custom")
.setLabel("Custom")
.setEmoji(getEmojiByName("TICKETS.OTHER", "id"))
.setStyle(ButtonStyle.Primary)
])
]
});
let i: MessageComponentInteraction;
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) {
innerTimedOut = true;
continue;
}
if (i.isStringSelectMenu() && i.customId === "template") {
await i.deferUpdate();
await interaction.channel!.send({
embeds: [
new EmojiEmbed()
.setTitle(verifyMessages[parseInt(i.values[0]!)]!.label)
.setDescription(
verifyMessages[parseInt(i.values[0]!)]!.description
)
.setStatus("Success")
.setEmoji("CONTROL.BLOCKTICK")
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setLabel("Verify")
.setEmoji(getEmojiByName("CONTROL.TICK", "id"))
.setStyle(ButtonStyle.Success)
.setCustomId("verifybutton")
])
]
});
templateSelected = true;
continue;
} else if ((i.component as ButtonComponent).customId === "blank") {
await i.deferUpdate();
await interaction.channel!.send({
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setLabel("Verify")
.setEmoji(getEmojiByName("CONTROL.TICK", "id"))
.setStyle(ButtonStyle.Success)
.setCustomId("verifybutton")
])
]
});
templateSelected = true;
continue;
} else if ((i.component as ButtonComponent).customId === "custom") {
await i.showModal(
new Discord.ModalBuilder()
.setCustomId("modal")
.setTitle("Enter embed details")
.addComponents(
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("title")
.setLabel("Title")
.setMaxLength(256)
.setRequired(true)
.setStyle(Discord.TextInputStyle.Short)
),
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("description")
.setLabel("Description")
.setMaxLength(4000)
.setRequired(true)
.setStyle(Discord.TextInputStyle.Paragraph)
)
)
);
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Verify Button")
.setDescription("Modal opened. If you can't see it, click back and try again.")
.setStatus("Success")
.setEmoji("GUILD.TICKET.OPEN")
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setLabel("Back")
.setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
.setStyle(ButtonStyle.Primary)
.setCustomId("back")
])
]
});
let out;
try {
out = await modalInteractionCollector(
m,
(m: Interaction) =>
(m as MessageComponentInteraction | ModalSubmitInteraction).channelId ===
interaction.channelId,
(m) => m.customId === "modify"
);
} catch (e) {
innerTimedOut = true;
continue;
}
if (out !== null && out instanceof ModalSubmitInteraction) {
const title = out.fields.getTextInputValue("title");
const description = out.fields.getTextInputValue("description");
await interaction.channel!.send({
embeds: [
new EmojiEmbed()
.setTitle(title)
.setDescription(description)
.setStatus("Success")
.setEmoji("CONTROL.BLOCKTICK")
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setLabel("Verify")
.setEmoji(getEmojiByName("CONTROL.TICK", "id"))
.setStyle(ButtonStyle.Success)
.setCustomId("verifybutton")
])
]
});
templateSelected = true;
}
}
}
} else {
await i.deferUpdate();
break;
}
}
} // TODO: On save, clear SEN
await interaction.editReply({
embeds: [new EmbedBuilder(m.embeds[0]!.data).setFooter({ text: "Message closed" })],
components: []

@ -173,11 +173,8 @@ async function userAbout(guild: Discord.Guild, member: Discord.GuildMember, inte
generateKeyValueList({
member: renderUser(member.user),
id: `\`${member.id}\``,
roles: `${member.roles.cache.size - 1}` // FIXME
}) +
"\n" +
(s.length > 0 ? s : "*None*") +
"\n"
roles: `${member.roles.cache.size - 1}`
}) + "\n" + (s.length > 0 ? s : "*None*") + "\n"
)
)
.setTitle("Roles")

@ -4,14 +4,7 @@ import client from "../../utils/client.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import { LoadingEmbed } from "../../utils/defaults.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
const listToAndMore = (list: string[], max: number) => {
// PineappleFan, Coded, Mini (and 10 more)
if(list.length > max) {
return list.slice(0, max).join(", ") + ` (and ${list.length - max} more)`;
}
return list.join(", ");
}
import listToAndMore from "../../utils/listToAndMore.js"
const { renderUser } = client.logger;

@ -98,9 +98,8 @@
"TITLEUPDATE": "729763053620691044",
"TOPICUPDATE": "729763053477953536",
"SLOWMODE": {
"ON": "777138171301068831",
"OFF": "777138171447869480",
"// TODO": "Make these green and red respectively"
"ON": "973616021304913950",
"OFF": "777138171447869480"
},
"NSFW": {
"ON": "729064531208175736",

@ -43,7 +43,7 @@ export async function callback(client: NucleusClient, before: GuildMember, after
log(data);
} else if (
(before.communicationDisabledUntilTimestamp ?? 0) < new Date().getTime() &&
(after.communicationDisabledUntil ?? 0) > new Date().getTime() // TODO: test this
(after.communicationDisabledUntil ?? 0) > new Date().getTime()
) {
await client.database.history.create(
"mute",

@ -32,6 +32,12 @@ export async function callback(client: NucleusClient, oldRole: Role, newRole: Ro
changes["mentionable"] = entry([oldRole.mentionable, newRole.mentionable], `${mentionable[0]} -> ${mentionable[1]}`);
if (oldRole.hexColor !== newRole.hexColor)
changes["color"] = entry([oldRole.hexColor, newRole.hexColor], `\`${oldRole.hexColor}\` -> \`${newRole.hexColor}\``);
if (oldRole.permissions.bitfield !== newRole.permissions.bitfield) {
changes["permissions"] = entry(
[oldRole.permissions.bitfield.toString(), newRole.permissions.bitfield.toString()],
`[[Old]](https://discordapi.com/permissions.html#${oldRole.permissions.bitfield.toString()}) -> [[New]](https://discordapi.com/permissions.html#${newRole.permissions.bitfield.toString()})`
);
}
if (Object.keys(changes).length === 4) return;
@ -48,6 +54,6 @@ export async function callback(client: NucleusClient, oldRole: Role, newRole: Ro
hidden: {
guild: newRole.guild.id
}
}; // TODO: show perms changed (webpage)
}; // TODO: make our own page for this
log(data);
}

@ -43,7 +43,7 @@ export default async function logAttachment(message: Message): Promise<Attachmen
);
return { files: attachments };
}
const channelObj = await client.channels.fetch(channel);
const channelObj = await message.guild.channels.fetch(channel);
if (!channelObj) {
singleNotify(
"attachmentLogChannelDeleted",

@ -8,9 +8,7 @@ import {
TextChannel,
ButtonStyle,
User,
ComponentType,
APIBaseSelectMenuComponent,
SelectMenuComponent
ComponentType
} from "discord.js";
import EmojiEmbed from "../utils/generateEmojiEmbed.js";
import getEmojiByName from "../utils/getEmojiByName.js";
@ -54,6 +52,13 @@ interface TranscriptAuthor {
}
}
interface TranscriptAttachment {
url: string;
filename: string;
size: number;
log?: string;
}
interface TranscriptMessage {
id: string;
author: TranscriptAuthor;
@ -63,7 +68,7 @@ interface TranscriptMessage {
editedTimestamp?: number;
createdTimestamp: number;
flags?: string[];
attachments?: unknown; //FIXME
attachments?: TranscriptAttachment[];
stickerURLs?: string[];
referencedMessage?: string | [string, string, string];
}

@ -73,7 +73,7 @@ export default async (guild: Guild, interaction?: CommandInteraction) => {
"**General:** These are events like kicks and channel changes etc.\n" +
`> These are standard logs and can be set with ${getCommandMentionByName("settings/logs/general")}\n` +
"**Warnings:** Warnings like NSFW avatars and spam etc. that may require action by a server staff member.\n" +
`> These may require special action by a moderator. You can set the channel with ${getCommandMentionByName("settings/logs/warnings")}\n` + // TODO
`> These may require special action by a moderator. You can set the channel with ${getCommandMentionByName("settings/logs/warnings")}\n` +
"**Attachments:** All images sent in the server - Used to keep a record of deleted images\n" +
`> Sent to a separate log channel to avoid spam. This can be set with ${getCommandMentionByName("settings/logs/attachments")}\n` +
`> ${getEmojiByName("NUCLEUS.PREMIUM")} Please note this feature is only available with ${getCommandMentionByName("nucleus/premium")}`

@ -13,6 +13,8 @@ import fetch from "node-fetch";
import { TestString, NSFWCheck } from "./scanners.js";
import createPageIndicator from "../utils/createPageIndicator.js";
import client from "../utils/client.js";
import singleNotify from "../utils/singleNotify.js";
import { getCommandMentionByName } from "../utils/getCommandDataByName.js";
export interface VerifySchema {
uID: string;
@ -206,7 +208,8 @@ export default async function (interaction: CommandInteraction | ButtonInteracti
.setEmoji("CONTROL.BLOCKCROSS")
]
});
return; // TODO: SEN
singleNotify("verifyRoleDeleted", interaction.guild!.id, `The role given when a member is verified has been deleted. Use ${getCommandMentionByName("settings/verify")} to set a new one`, "Critical")
return;
}
verify[code] = {
uID: interaction.member!.user.id,

@ -168,7 +168,7 @@ export class ScanCache {
}
async write(hash: string, data: boolean, tags?: string[]) {
await this.scanCache.insertOne({ hash: hash, data: data, tags: tags ?? [], addedAt: new Date() }); // TODO: cleanup function maybe
await this.scanCache.insertOne({ hash: hash, data: data, tags: tags ?? [], addedAt: new Date() });
}
async cleanup() {

@ -0,0 +1,7 @@
export default (list: string[], max: number) => {
// PineappleFan, Coded, Mini (and 10 more)
if(list.length > max) {
return list.slice(0, max).join(", ") + ` (and ${list.length - max} more)`;
}
return list.join(", ");
}
Loading…
Cancel
Save