possibly finished current features

pull/11/head
TheCodedProf 3 years ago
parent 1f67504a24
commit 01cba76c6d

@ -3,7 +3,6 @@ import type { HistorySchema } from "../../utils/database.js";
import Discord, {
CommandInteraction,
GuildMember,
Interaction,
Message,
ActionRowBuilder,
ButtonBuilder,
@ -401,12 +400,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
});
let out;
try {
out = await modalInteractionCollector(
m,
(m: Interaction) =>
(m as MessageComponentInteraction | ModalSubmitInteraction).channelId === interaction.channelId,
(m) => m.customId === "modify"
);
out = await modalInteractionCollector(m, interaction.user);
} catch (e) {
timedOut = true;
continue;

@ -8,11 +8,8 @@ import { ActionRowBuilder,
ChannelSelectMenuBuilder,
ChannelSelectMenuInteraction,
CommandInteraction,
Interaction,
Message,
MessageComponentInteraction,
ModalBuilder,
ModalSubmitInteraction,
RoleSelectMenuBuilder,
RoleSelectMenuInteraction,
StringSelectMenuBuilder,
@ -317,12 +314,7 @@ const wordMenu = async (interaction: StringSelectMenuInteraction, m: Message, cu
await i.showModal(modal);
let out;
try {
out = await modalInteractionCollector(
m,
(m: Interaction) =>
(m as MessageComponentInteraction | ModalSubmitInteraction).channelId === interaction.channelId,
(m) => m.customId === "back"
);
out = await modalInteractionCollector(m, interaction.user);
} catch (e) {
break;
}
@ -612,12 +604,7 @@ const mentionMenu = async (interaction: StringSelectMenuInteraction, m: Message,
await i.showModal(modal);
let out;
try {
out = await modalInteractionCollector(
m,
(m: Interaction) =>
(m as MessageComponentInteraction | ModalSubmitInteraction).channelId === interaction.channelId,
(m) => m.customId === "back"
);
out = await modalInteractionCollector(m, interaction.user);
} catch (e) {
break;
}
@ -912,7 +899,8 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
}
}
} while(!closed)
} while(!closed);
await interaction.deleteReply()
};

@ -1,195 +1,105 @@
import { LoadingEmbed } from "../../../utils/defaults.js";
import { ChannelType } from "discord-api-types/v9";
import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, ButtonInteraction } from "discord.js";
import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelSelectMenuBuilder } from "discord.js";
import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
import confirmationMessage from "../../../utils/confirmationMessage.js";
import getEmojiByName from "../../../utils/getEmojiByName.js";
import type { SlashCommandSubcommandBuilder } from "discord.js";
import client from "../../../utils/client.js";
import { getCommandMentionByName } from "../../../utils/getCommandDataByName.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder
.setName("attachments")
.setDescription("Where attachments should be logged to (Premium only)")
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("The channel to log attachments in")
.addChannelTypes(ChannelType.GuildText)
.setRequired(false)
);
const callback = async (interaction: CommandInteraction): Promise<unknown> => {
const m = (await interaction.reply({
await interaction.reply({
embeds: LoadingEmbed,
ephemeral: true,
fetchReply: true
})) as Discord.Message;
if (interaction.options.get("channel")?.channel) {
let channel;
try {
channel = interaction.options.get("channel")?.channel;
} catch {
return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("CHANNEL.TEXT.DELETE")
.setTitle("Attachment Log Channel")
.setDescription("The channel you provided is not a valid channel")
.setStatus("Danger")
]
});
}
channel = channel as Discord.TextChannel;
if (channel.guild.id !== interaction.guild!.id) {
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Attachment Log Channel")
.setDescription("You must choose a channel in this server")
.setStatus("Danger")
.setEmoji("CHANNEL.TEXT.DELETE")
]
});
}
const confirmation = await new confirmationMessage(interaction)
.setEmoji("CHANNEL.TEXT.EDIT")
.setTitle("Attachment Log Channel")
})
if(!client.database.premium.hasPremium(interaction.guild!.id)) return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Premium Required")
.setDescription(`This feature is exclusive to ${getCommandMentionByName("nucleus/premium")} servers.`)
.setStatus("Danger")
.setEmoji("NUCLEUS.PREMIUM")
]
});
let data = await client.database.guilds.read(interaction.guild!.id);
let channel = data.logging.staff.channel;
let closed = false;
do {
const channelMenu = new ActionRowBuilder<ChannelSelectMenuBuilder>()
.addComponents(
new ChannelSelectMenuBuilder()
.setCustomId("channel")
.setPlaceholder("Select a channel")
);
const buttons = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("clear")
.setLabel("Clear")
.setStyle(ButtonStyle.Danger)
.setEmoji(getEmojiByName("CONTROL.CROSS", "id") as Discord.APIMessageComponentEmoji)
.setDisabled(!channel),
new ButtonBuilder()
.setCustomId("save")
.setLabel("Save")
.setStyle(ButtonStyle.Success)
.setEmoji(getEmojiByName("ICONS.SAVE", "id") as Discord.APIMessageComponentEmoji)
.setDisabled(channel === data.logging.staff.channel)
);
const embed = new EmojiEmbed()
.setTitle("Attachments")
.setDescription(
"This will be the channel all attachments will be sent to.\n\n" +
`Are you sure you want to set the attachment log channel to <#${channel.id}>?`
`The channel to send all attachments from the server, allowing you to check them if they are deleted` +
`**Channel:** ${channel ? `<#${channel}>` : "*None*"}\n`
)
.setColor("Warning")
.setFailedMessage("No changes were made", "Success", "CHANNEL.TEXT.CREATE")
.setInverted(true)
.send(true);
if (confirmation.cancelled) return;
if (confirmation.success) {
try {
await client.database.guilds.write(interaction.guild!.id, {
"logging.attachments.channel": channel.id
});
const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger;
const data = {
meta: {
type: "attachmentChannelUpdate",
displayName: "Attachment Log Channel Updated",
calculateType: "nucleusSettingsUpdated",
color: NucleusColors.yellow,
emoji: "CHANNEL.TEXT.EDIT",
timestamp: new Date().getTime()
},
list: {
memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
changedBy: entry(interaction.user.id, renderUser(interaction.user)),
channel: entry(channel.id, renderChannel(channel))
},
hidden: {
guild: interaction.guild!.id
}
};
log(data);
} catch (e) {
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Attachment Log Channel")
.setDescription("Something went wrong and the attachment log channel could not be set")
.setStatus("Danger")
.setEmoji("CHANNEL.TEXT.DELETE")
],
components: []
});
}
} else {
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Attachment Log Channel")
.setDescription("No changes were made")
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
],
components: []
});
}
}
let clicks = 0;
const data = await client.database.guilds.read(interaction.guild!.id);
let channel = data.logging.staff.channel;
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
let timedOut = false;
while (!timedOut) {
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Attachment Log Channel")
.setDescription(
channel
? `Your attachment log channel is currently set to <#${channel}>`
: "This server does not have an attachment log channel" +
(await client.database.premium.hasPremium(interaction.guild!.id)
? ""
: "\n\nThis server does not have premium, so this feature is disabled")
)
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setCustomId("clear")
.setLabel(clicks ? "Click again to confirm" : "Reset channel")
.setEmoji(getEmojiByName(clicks ? "TICKETS.ISSUE" : "CONTROL.CROSS", "id"))
.setStyle(ButtonStyle.Danger)
.setDisabled(!channel)
])
]
embeds: [embed],
components: [channelMenu, buttons]
});
let i;
let i: Discord.ButtonInteraction | Discord.SelectMenuInteraction;
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 }
});
i = (await interaction.channel!.awaitMessageComponent({
filter: (i) => i.user.id === interaction.user.id,
time: 300000
})) as Discord.ButtonInteraction | Discord.SelectMenuInteraction;
} catch (e) {
timedOut = true;
continue;
closed = true;
break;
}
await i.deferUpdate();
if ((i.component as unknown as ButtonInteraction).customId === "clear") {
clicks ++;
if (clicks === 2) {
clicks = 0;
await client.database.guilds.write(interaction.guild!.id, null, ["logging.announcements.channel"]);
channel = null;
if(i.isButton()) {
switch (i.customId) {
case "clear": {
channel = null;
break;
}
case "save": {
await client.database.guilds.write(interaction.guild!.id, {
"logging.attachments.channel": channel
});
data = await client.database.guilds.read(interaction.guild!.id);
break;
}
}
} else {
channel = i.values[0]!;
}
}
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Attachment Log Channel")
.setDescription(
channel
? `Your attachment log channel is currently set to <#${channel}>`
: "This server does not have an attachment log channel"
)
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
.setFooter({ text: "Message closed" })
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setCustomId("clear")
.setLabel("Clear")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
.setStyle(ButtonStyle.Secondary)
.setDisabled(true)
])
]
});
} while (!closed);
await interaction.deleteReply()
};
const check = (interaction: CommandInteraction, _partial: boolean = false) => {

@ -1,9 +1,11 @@
import { LoadingEmbed } from "../../../utils/defaults.js";
import Discord, { CommandInteraction, Message, ActionRowBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuBuilder, EmbedBuilder } from "discord.js";
import { SlashCommandSubcommandBuilder, StringSelectMenuOptionBuilder } from "discord.js";
import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
import Discord, { CommandInteraction, ActionRowBuilder, ChannelSelectMenuBuilder, ChannelType, ButtonBuilder, ButtonStyle, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, ButtonInteraction, StringSelectMenuInteraction, ChannelSelectMenuInteraction, APIMessageComponentEmoji } from "discord.js";
import type { SlashCommandSubcommandBuilder } from "discord.js";
import client from "../../../utils/client.js";
import compare from "lodash";
import { toHexArray, toHexInteger } from "../../../utils/calculate.js";
import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
import getEmojiByName from "../../../utils/getEmojiByName.js";
const logs: Record<string, string> = {
channelUpdate: "Channels created, deleted or modified",
@ -24,85 +26,135 @@ const logs: Record<string, string> = {
webhookUpdate: "Webhooks created or deleted",
guildMemberVerify: "Member runs verify",
autoModeratorDeleted: "Messages auto deleted by Nucleus",
nucleusSettingsUpdated: "Nucleus' settings updated by a moderator",
ticketUpdate: "Tickets created or deleted"
ticketUpdate: "Tickets created or deleted",
//nucleusSettingsUpdated: "Nucleus' settings updated by a moderator" // TODO
};
const command = (builder: SlashCommandSubcommandBuilder) =>
builder.setName("events").setDescription("Sets what events should be logged");
builder
.setName("events")
.setDescription("The general log channel for the server, and setting what events to show")
const callback = async (interaction: CommandInteraction): Promise<void> => {
await interaction.reply({
const m = (await interaction.reply({
embeds: LoadingEmbed,
fetchReply: true,
ephemeral: true
});
let m: Message;
let timedOut = false;
ephemeral: true,
fetchReply: true
})) as Discord.Message;
let config = await client.database.guilds.read(interaction.guild!.id);
let data = Object.assign({}, config.logging.logs);
let closed = false;
let show = false;
do {
const config = await client.database.guilds.read(interaction.guild!.id);
const converted = toHexArray(config.logging.logs.toLog);
const selectPane = new StringSelectMenuBuilder()
.setPlaceholder("Set events to log")
.setMaxValues(Object.keys(logs).length)
.setCustomId("logs")
.setMinValues(0)
Object.keys(logs).map((e, i) => {
selectPane.addOptions(new StringSelectMenuOptionBuilder()
.setLabel(logs[e]!)
.setValue(i.toString())
.setDefault(converted.includes(e))
const channelMenu = new ActionRowBuilder<ChannelSelectMenuBuilder>()
.addComponents(
new ChannelSelectMenuBuilder()
.setCustomId("channel")
.setPlaceholder("Select a channel")
.setChannelTypes(ChannelType.GuildText)
)
const buttons = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("switch")
.setLabel(data.enabled ? "Enabled" : "Disabled")
.setStyle(data.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
.setEmoji(getEmojiByName((data.enabled ? "CONTROL.TICK" : "CONTROL.CROSS"), "id") as APIMessageComponentEmoji),
new ButtonBuilder()
.setCustomId("remove")
.setLabel("Remove")
.setStyle(ButtonStyle.Danger)
.setDisabled(!data.channel),
new ButtonBuilder()
.setCustomId("show")
.setLabel("Manage Events")
.setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId("save")
.setLabel("Save")
.setStyle(ButtonStyle.Success)
.setDisabled(compare.isEqual(data, config.logging.logs))
)
const converted = toHexArray(data.toLog);
const toLogMenu = new ActionRowBuilder<StringSelectMenuBuilder>()
.addComponents(
new StringSelectMenuBuilder()
.setPlaceholder("Set events to log")
.setMaxValues(Object.keys(logs).length)
.setCustomId("logs")
.setMinValues(0)
)
Object.keys(logs).map((e) => {
toLogMenu.components[0]!.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel(logs[e]!)
.setValue(e)
.setDefault(converted.includes(e))
)
});
m = (await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Logging Events")
.setDescription(
"Below are the events being logged in the server. You can toggle them on and off in the dropdown"
)
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
],
components: [
new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(selectPane),
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder().setLabel("Select all").setStyle(ButtonStyle.Primary).setCustomId("all"),
new ButtonBuilder().setLabel("Select none").setStyle(ButtonStyle.Danger).setCustomId("none")
])
]
})) as Message;
let i;
const embed = new EmojiEmbed()
.setTitle("General Log Channel")
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
.setDescription(
`This is the channel that all events you set to be logged will be stored\n` +
`**Channel:** ${data.channel ? `<#${data.channel}>` : "None"}\n`
)
let components: ActionRowBuilder<ButtonBuilder | ChannelSelectMenuBuilder | StringSelectMenuBuilder>[] = [channelMenu, buttons];
if(show) components.push(toLogMenu);
await interaction.editReply({
embeds: [embed],
components: components
});
let i: ButtonInteraction | StringSelectMenuInteraction | ChannelSelectMenuInteraction;
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 }
});
filter: (i) => i.user.id === interaction.user.id,
time: 300000
}) as ButtonInteraction | StringSelectMenuInteraction | ChannelSelectMenuInteraction;
} catch (e) {
timedOut = true;
continue;
closed = true;
break;
}
await i.deferUpdate();
if (i.isStringSelectMenu() && i.customId === "logs") {
const selected = i.values;
const newLogs = toHexInteger(selected.map((e: string) => Object.keys(logs)[parseInt(e)]!));
await client.database.guilds.write(interaction.guild!.id, {
"logging.logs.toLog": newLogs
});
} else if (i.customId === "all") {
const newLogs = toHexInteger(Object.keys(logs).map((e) => e));
await client.database.guilds.write(interaction.guild!.id, {
"logging.logs.toLog": newLogs
});
} else if (i.customId === "none") {
await client.database.guilds.write(interaction.guild!.id, {
"logging.logs.toLog": 0
});
if(i.isButton()) {
switch(i.customId) {
case "show": {
show = !show;
break;
}
case "switch": {
data.enabled = !data.enabled;
break;
}
case "save": {
await client.database.guilds.write(interaction.guild!.id, {"logging.logs": data});
config = await client.database.guilds.read(interaction.guild!.id);
data = Object.assign({}, config.logging.logs);
break;
}
case "remove": {
data.channel = null;
break;
}
}
} else if(i.isStringSelectMenu()) {
let hex = toHexInteger(i.values);
data.toLog = hex;
} else if(i.isChannelSelectMenu()) {
data.channel = i.values[0]!;
}
} while (!timedOut);
await interaction.editReply({ embeds: [new EmbedBuilder(m.embeds[0]!.data).setFooter({ text: "Message timed out" })] });
return;
} while (!closed);
await interaction.deleteReply()
};
const check = (interaction: CommandInteraction, _partial: boolean = false) => {

@ -1,197 +0,0 @@
import { LoadingEmbed } from "../../../utils/defaults.js";
import { ChannelType } from "discord-api-types/v9";
import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, ButtonInteraction, ButtonComponent } from "discord.js";
import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
import confirmationMessage from "../../../utils/confirmationMessage.js";
import getEmojiByName from "../../../utils/getEmojiByName.js";
import type { SlashCommandSubcommandBuilder } from "discord.js";
import client from "../../../utils/client.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder
.setName("general")
.setDescription("Sets or shows the log channel")
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("The channel to set the log channel to")
.addChannelTypes(ChannelType.GuildText)
);
const callback = async (interaction: CommandInteraction): Promise<unknown> => {
const m = (await interaction.reply({
embeds: LoadingEmbed,
ephemeral: true,
fetchReply: true
})) as Discord.Message;
if (interaction.options.get("channel")?.channel) {
let channel;
try {
channel = interaction.options.get("channel")?.channel;
} catch {
return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("CHANNEL.TEXT.DELETE")
.setTitle("Log Channel")
.setDescription("The channel you provided is not a valid channel")
.setStatus("Danger")
]
});
}
channel = channel as Discord.TextChannel;
if (channel.guild.id !== interaction.guild!.id) {
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Log Channel")
.setDescription("You must choose a channel in this server")
.setStatus("Danger")
.setEmoji("CHANNEL.TEXT.DELETE")
]
});
}
const confirmation = await new confirmationMessage(interaction)
.setEmoji("CHANNEL.TEXT.EDIT")
.setTitle("Log Channel")
.setDescription(`Are you sure you want to set the log channel to <#${channel.id}>?`)
.setColor("Warning")
.setFailedMessage("No changes were made", "Success", "CHANNEL.TEXT.CREATE")
.setInverted(true)
.send(true);
if (confirmation.cancelled) return;
if (confirmation.success) {
try {
await client.database.guilds.write(interaction.guild!.id, {
"logging.logs.channel": channel.id
});
const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger;
const data = {
meta: {
type: "logChannelUpdate",
displayName: "Log Channel Changed",
calculateType: "nucleusSettingsUpdated",
color: NucleusColors.yellow,
emoji: "CHANNEL.TEXT.EDIT",
timestamp: new Date().getTime()
},
list: {
memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
changedBy: entry(interaction.user.id, renderUser(interaction.user)),
channel: entry(channel.id, renderChannel(channel))
},
hidden: {
guild: channel.guild.id
}
};
log(data);
} catch (e) {
console.log(e);
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Log Channel")
.setDescription("Something went wrong and the log channel could not be set")
.setStatus("Danger")
.setEmoji("CHANNEL.TEXT.DELETE")
],
components: []
});
}
} else {
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Log Channel")
.setDescription("No changes were made")
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
],
components: []
});
}
}
let clicks = 0;
const data = await client.database.guilds.read(interaction.guild!.id);
let channel = data.logging.logs.channel;
let timedOut = false;
while (!timedOut) {
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Log channel")
.setDescription(
channel
? `Your log channel is currently set to <#${channel}>`
: "This server does not have a log channel"
)
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setCustomId("clear")
.setLabel(clicks ? "Click again to confirm" : "Reset channel")
.setEmoji(getEmojiByName(clicks ? "TICKETS.ISSUE" : "CONTROL.CROSS", "id"))
.setStyle(ButtonStyle.Danger)
.setDisabled(!channel)
])
]
});
let i: ButtonInteraction;
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 }
}) as ButtonInteraction;
} catch (e) {
timedOut = true;
}
i = i!
await i.deferUpdate();
if ((i.component as ButtonComponent).customId === "clear") {
clicks ++;
if (clicks === 2) {
clicks = 0;
await client.database.guilds.write(interaction.guild!.id, null, ["logging.logs.channel"]);
channel = null;
}
}
}
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Log channel")
.setDescription(
channel
? `Your log channel is currently set to <#${channel}>`
: "This server does not have a log channel"
)
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
.setFooter({ text: "Message closed" })
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setCustomId("clear")
.setLabel("Clear")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
.setStyle(ButtonStyle.Secondary)
.setDisabled(true)
])
]
});
};
const check = (interaction: CommandInteraction, _partial: boolean = false) => {
const member = interaction.member as Discord.GuildMember;
if (!member.permissions.has("ManageGuild"))
return "You must have the *Manage Server* permission to use this command";
return true;
};
export { command };
export { callback };
export { check };

@ -1,8 +1,6 @@
import { LoadingEmbed } from "../../../utils/defaults.js";
import { ChannelType } from "discord-api-types/v9";
import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, ButtonComponent } from "discord.js";
import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelSelectMenuBuilder } from "discord.js";
import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
import confirmationMessage from "../../../utils/confirmationMessage.js";
import getEmojiByName from "../../../utils/getEmojiByName.js";
import type { SlashCommandSubcommandBuilder } from "discord.js";
import client from "../../../utils/client.js";
@ -11,182 +9,86 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
builder
.setName("warnings")
.setDescription("Settings for the staff notifications channel")
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("The channel to set the staff notifications channel to")
.addChannelTypes(ChannelType.GuildText)
.setRequired(false)
);
const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (!interaction.guild) return;
const m = (await interaction.reply({
await interaction.reply({
embeds: LoadingEmbed,
ephemeral: true,
fetchReply: true
})) as Discord.Message;
if (interaction.options.get("channel")?.channel) {
let channel;
try {
channel = interaction.options.get("channel")?.channel;
} catch {
return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("CHANNEL.TEXT.DELETE")
.setTitle("Staff Notifications Channel")
.setDescription("The channel you provided is not a valid channel")
.setStatus("Danger")
]
});
}
channel = channel as Discord.TextChannel;
if (channel.guild.id !== interaction.guild.id) {
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Staff Notifications Channel")
.setDescription("You must choose a channel in this server")
.setStatus("Danger")
.setEmoji("CHANNEL.TEXT.DELETE")
]
});
}
const confirmation = await new confirmationMessage(interaction)
.setEmoji("CHANNEL.TEXT.EDIT")
})
let data = await client.database.guilds.read(interaction.guild.id);
let channel = data.logging.staff.channel;
let closed = false;
do {
const channelMenu = new ActionRowBuilder<ChannelSelectMenuBuilder>()
.addComponents(
new ChannelSelectMenuBuilder()
.setCustomId("channel")
.setPlaceholder("Select a channel")
);
const buttons = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("clear")
.setLabel("Clear")
.setStyle(ButtonStyle.Danger)
.setEmoji(getEmojiByName("CONTROL.CROSS", "id") as Discord.APIMessageComponentEmoji)
.setDisabled(!channel),
new ButtonBuilder()
.setCustomId("save")
.setLabel("Save")
.setStyle(ButtonStyle.Success)
.setEmoji(getEmojiByName("ICONS.SAVE", "id") as Discord.APIMessageComponentEmoji)
.setDisabled(channel === data.logging.staff.channel)
);
const embed = new EmojiEmbed()
.setTitle("Staff Notifications Channel")
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
.setDescription(
"This will be the channel all notifications, updates, user reports etc. will be sent to.\n\n" +
`Are you sure you want to set the staff notifications channel to <#${channel.id}>?`
`Logs which require an action from a moderator or administrator will be sent to this channel.\n` +
`**Channel:** ${channel ? `<#${channel}>` : "*None*"}\n`
)
.setColor("Warning")
.setFailedMessage("No changes were made", "Success", "CHANNEL.TEXT.CREATE")
.setInverted(true)
.send(true);
if (confirmation.cancelled) return;
if (confirmation.success) {
try {
await client.database.guilds.write(interaction.guild.id, {
"logging.staff.channel": channel.id
});
const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger;
const data = {
meta: {
type: "staffChannelUpdate",
displayName: "Staff Notifications Channel Updated",
calculateType: "nucleusSettingsUpdated",
color: NucleusColors.yellow,
emoji: "CHANNEL.TEXT.EDIT",
timestamp: new Date().getTime()
},
list: {
memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
changedBy: entry(interaction.user.id, renderUser(interaction.user)),
channel: entry(channel.id, renderChannel(channel))
},
hidden: {
guild: interaction.guild.id
}
};
log(data);
} catch (e) {
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Staff Notifications Channel")
.setDescription("Something went wrong and the staff notifications channel could not be set")
.setStatus("Danger")
.setEmoji("CHANNEL.TEXT.DELETE")
],
components: []
});
}
} else {
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Staff Notifications Channel")
.setDescription("No changes were made")
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
],
components: []
});
}
}
let clicks = 0;
const data = await client.database.guilds.read(interaction.guild.id);
let channel = data.logging.staff.channel;
let timedOut = false;
while (!timedOut) {
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Staff Notifications channel")
.setDescription(
channel
? `Your staff notifications channel is currently set to <#${channel}>`
: "This server does not have a staff notifications channel"
)
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setCustomId("clear")
.setLabel(clicks ? "Click again to confirm" : "Reset channel")
.setEmoji(getEmojiByName(clicks ? "TICKETS.ISSUE" : "CONTROL.CROSS", "id"))
.setStyle(ButtonStyle.Danger)
.setDisabled(!channel)
])
]
embeds: [embed],
components: [channelMenu, buttons]
});
let i;
let i: Discord.ButtonInteraction | Discord.SelectMenuInteraction;
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 }
});
i = (await interaction.channel!.awaitMessageComponent({
filter: (i) => i.user.id === interaction.user.id,
time: 300000
})) as Discord.ButtonInteraction | Discord.SelectMenuInteraction;
} catch (e) {
timedOut = true;
continue;
closed = true;
break;
}
await i.deferUpdate();
if ((i.component as ButtonComponent).customId === "clear") {
clicks ++;
if (clicks === 2) {
clicks = 0;
await client.database.guilds.write(interaction.guild.id, null, ["logging.staff.channel"]);
channel = null;
if(i.isButton()) {
switch (i.customId) {
case "clear": {
channel = null;
break;
}
case "save": {
await client.database.guilds.write(interaction.guild!.id, {
"logging.warnings.channel": channel
});
data = await client.database.guilds.read(interaction.guild!.id);
break;
}
}
} else {
channel = i.values[0]!;
}
}
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Staff Notifications channel")
.setDescription(
channel
? `Your staff notifications channel is currently set to <#${channel}>`
: "This server does not have a staff notifications channel"
)
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
.setFooter({ text: "Message closed" })
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setCustomId("clear")
.setLabel("Clear")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
.setStyle(ButtonStyle.Secondary)
.setDisabled(true)
])
]
});
} while (!closed);
await interaction.deleteReply()
};
const check = (interaction: CommandInteraction, _partial: boolean = false) => {

@ -1,5 +1,5 @@
import { LoadingEmbed } from "../../utils/defaults.js";
import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, ButtonComponent, TextInputBuilder, Message, RoleSelectMenuBuilder } from "discord.js";
import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, ButtonComponent, TextInputBuilder, RoleSelectMenuBuilder } from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import type { SlashCommandSubcommandBuilder } from "discord.js";
@ -12,17 +12,16 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.setDescription("Links and text shown to a user after a moderator action is performed")
const callback = async (interaction: CommandInteraction): Promise<void> => {
await interaction.reply({
const m = await interaction.reply({
embeds: LoadingEmbed,
ephemeral: true,
fetchReply: true
});
let m: Message;
let timedOut = false;
while (!timedOut) {
const config = await client.database.guilds.read(interaction.guild!.id);
const moderation = config.moderation;
m = await interaction.editReply({
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Moderation Commands")
@ -152,11 +151,7 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
});
let out: Discord.ModalSubmitInteraction | null;
try {
out = await modalInteractionCollector(
m,
(m) => m.channel!.id === interaction.channel!.id,
(_) => true
) as Discord.ModalSubmitInteraction | null;
out = await modalInteractionCollector(m, interaction.user) as Discord.ModalSubmitInteraction | null;
} catch (e) {
continue;
}
@ -175,6 +170,7 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
}
}
}
await interaction.deleteReply()
};
const check = (interaction: CommandInteraction, _partial: boolean = false) => {

@ -144,11 +144,7 @@ const editNameDescription = async (i: ButtonInteraction, interaction: StringSele
let out: Discord.ModalSubmitInteraction | null;
try {
out = await modalInteractionCollector(
m,
(m) => m.channel!.id === interaction.channel!.id,
(_) => true
) as Discord.ModalSubmitInteraction | null;
out = await modalInteractionCollector(m, interaction.user) as Discord.ModalSubmitInteraction | null;
} catch (e) {
console.error(e);
out = null;
@ -472,7 +468,8 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
}
}
} while (!closed)
} while (!closed);
await interaction.deleteReply()
};
const check = (interaction: CommandInteraction, _partial: boolean = false) => {

@ -74,7 +74,6 @@ const showModal = async (interaction: MessageComponentInteraction, current: { en
type ObjectSchema = Record<string, {name: string, enabled: boolean}>
const addStatsChannel = async (interaction: CommandInteraction, m: Message, currentObject: ObjectSchema): Promise<ObjectSchema> => {
let closed = false;
let cancelled = false;
@ -172,11 +171,7 @@ const addStatsChannel = async (interaction: CommandInteraction, m: Message, curr
});
showModal(i, {name: newChannelName, enabled: newChannelEnabled})
const out: Discord.ModalSubmitInteraction | ButtonInteraction| null = await modalInteractionCollector(
m,
(m) => m.channel!.id === interaction.channel!.id && m.user!.id === interaction.user!.id,
(i) => i.channel!.id === interaction.channel!.id && i.user!.id === interaction.user!.id && i.message!.id === m.id
);
const out: Discord.ModalSubmitInteraction | ButtonInteraction| null = await modalInteractionCollector(m, interaction.user);
if (!out) continue;
if (out.isButton()) continue;
newChannelName = out.fields.getTextInputValue("text");
@ -340,11 +335,7 @@ const callback = async (interaction: CommandInteraction) => {
});
let out: Discord.ModalSubmitInteraction | null;
try {
out = await modalInteractionCollector(
m,
(m) => m.channel!.id === interaction.channel!.id,
(_) => true
) as Discord.ModalSubmitInteraction | null;
out = await modalInteractionCollector(m, interaction.user) as Discord.ModalSubmitInteraction | null;
} catch (e) {
continue;
}
@ -396,6 +387,7 @@ const callback = async (interaction: CommandInteraction) => {
}
} while (!closed);
await interaction.deleteReply()
};
const check = (interaction: CommandInteraction, _partial: boolean = false) => {

@ -181,11 +181,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
});
let out;
try {
out = await modalInteractionCollector(
m,
(m) => m.user.id === interaction.user.id,
(m) => m.customId === "back"
);
out = await modalInteractionCollector(m, interaction.user);
} catch (e) {
continue;
}
@ -207,6 +203,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
}
}
}
await interaction.deleteReply()
};
@ -384,11 +381,7 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t
});
let out;
try {
out = await modalInteractionCollector(
m,
(m) => m.user.id === interaction.user.id,
(m) => m.customId === "back"
);
out = await modalInteractionCollector(m, interaction.user);
} catch (e) {
continue;
}

@ -63,11 +63,7 @@ const editName = async (i: ButtonInteraction, interaction: StringSelectMenuInter
let out: ModalSubmitInteraction | null;
try {
out = await modalInteractionCollector(
m,
(m) => m.channel!.id === interaction.channel!.id,
(_) => true
) as ModalSubmitInteraction | null;
out = await modalInteractionCollector(m, interaction.user) as ModalSubmitInteraction | null;
} catch (e) {
console.error(e);
out = null;
@ -440,7 +436,8 @@ const callback = async (interaction: CommandInteraction) => {
}
}
} while (!closed)
} while (!closed);
await interaction.deleteReply()
}
const check = (interaction: CommandInteraction, _partial: boolean = false) => {

@ -1,33 +1,25 @@
import { LoadingEmbed } from "../../utils/defaults.js";
import Discord, {
CommandInteraction,
Interaction,
Message,
ActionRowBuilder,
ButtonBuilder,
MessageComponentInteraction,
ModalSubmitInteraction,
Role,
ButtonStyle,
StringSelectMenuBuilder,
TextInputBuilder,
EmbedBuilder,
ButtonComponent
RoleSelectMenuBuilder,
APIMessageComponentEmoji
} from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import confirmationMessage from "../../utils/confirmationMessage.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 { getCommandMentionByName } from "../../utils/getCommandDataByName.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder
.setName("verify")
.setDescription("Manage the role given after typing /verify")
.addRoleOption((option) =>
option.setName("role").setDescription("The role to give after verifying").setRequired(false)
);
.setDescription("Manage the role given after a user runs /verify")
const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (!interaction.guild) return;
@ -36,153 +28,79 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
ephemeral: true,
fetchReply: true
})) as Message;
if (interaction.options.get("role")?.role) {
let role: Role;
try {
role = interaction.options.get("role")?.role as Role;
} catch {
return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("GUILD.ROLES.DELETE")
.setTitle("Verify Role")
.setDescription("The role you provided is not a valid role")
.setStatus("Danger")
]
});
}
role = role as Discord.Role;
if (role.guild.id !== interaction.guild.id) {
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Verify Role")
.setDescription("You must choose a role in this server")
.setStatus("Danger")
.setEmoji("GUILD.ROLES.DELETE")
]
});
}
const confirmation = await new confirmationMessage(interaction)
.setEmoji("GUILD.ROLES.EDIT")
let closed = false;
let config = await client.database.guilds.read(interaction.guild.id);
let data = Object.assign({}, config.verify);
do {
console.log(config.verify, data)
const selectMenu = new ActionRowBuilder<RoleSelectMenuBuilder>()
.addComponents(
new RoleSelectMenuBuilder()
.setCustomId("role")
.setPlaceholder("Select a role")
);
const buttons = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("switch")
.setLabel(data.enabled ? "Disabled" : "Enabled")
.setStyle(data.enabled ? ButtonStyle.Danger : ButtonStyle.Success)
.setEmoji(getEmojiByName(data.enabled ? "CONTROL.TICK" : "CONTROL.CROSS", "id") as APIMessageComponentEmoji),
new ButtonBuilder()
.setCustomId("save")
.setLabel("Save")
.setStyle(ButtonStyle.Success)
.setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji)
.setDisabled(data.role === config.verify.role && data.enabled === config.verify.enabled)
);
const embed = new EmojiEmbed()
.setTitle("Verify Role")
.setDescription(`Are you sure you want to set the verify role to <@&${role.id}>?`)
.setColor("Warning")
.setFailedMessage("No changes were made", "Warning", "GUILD.ROLES.DELETE")
.setInverted(true)
.send(true);
if (confirmation.cancelled) return;
if (confirmation.success) {
try {
await client.database.guilds.write(interaction.guild.id, {
"verify.role": role.id,
"verify.enabled": true
});
const { log, NucleusColors, entry, renderUser, renderRole } = client.logger;
const data = {
meta: {
type: "verifyRoleChanged",
displayName: "Verify Role Changed",
calculateType: "nucleusSettingsUpdated",
color: NucleusColors.green,
emoji: "CONTROL.BLOCKTICK",
timestamp: new Date().getTime()
},
list: {
memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
changedBy: entry(interaction.user.id, renderUser(interaction.user)),
role: entry(role.id, renderRole(role))
},
hidden: {
guild: interaction.guild.id
}
};
log(data);
} catch (e) {
console.log(e);
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Verify Role")
.setDescription("Something went wrong while setting the verify role")
.setStatus("Danger")
.setEmoji("GUILD.ROLES.DELETE")
],
components: []
});
}
} else {
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Verify Role")
.setDescription("No changes were made")
.setStatus("Success")
.setEmoji("GUILD.ROLES.CREATE")
],
components: []
});
}
}
let clicks = 0;
const data = await client.database.guilds.read(interaction.guild.id);
let role = data.verify.role;
.setDescription(
`Select a role to be given to users after they run ${getCommandMentionByName("verify")}` +
`\n\nCurrent role: ${config.verify.role ? `<@&${config.verify.role}>` : "None"}`
)
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE");
let timedOut = false;
while (!timedOut) {
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Verify Role")
.setDescription(
role ? `Your verify role is currently set to <@&${role}>` : "You have not set a verify role"
)
.setStatus("Success")
.setEmoji("GUILD.ROLES.CREATE")
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setCustomId("clear")
.setLabel(clicks ? "Click again to confirm" : "Reset role")
.setEmoji(getEmojiByName(clicks ? "TICKETS.ISSUE" : "CONTROL.CROSS", "id"))
.setStyle(ButtonStyle.Danger)
.setDisabled(!role),
new ButtonBuilder()
.setCustomId("send")
.setLabel("Add verify button")
.setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id"))
.setStyle(ButtonStyle.Primary)
])
]
embeds: [embed],
components: [selectMenu, buttons]
});
let i: MessageComponentInteraction;
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 }
filter: (i) => { return i.user.id === interaction.user.id }
});
} catch (e) {
timedOut = true;
closed = true;
continue;
}
await i.deferUpdate();
if ((i.component as ButtonComponent).customId === "clear") {
clicks ++;
if (clicks === 2) {
clicks = 0;
await client.database.guilds.write(interaction.guild.id, null, ["verify.role", "verify.enabled"]);
role = null;
if(i.isButton()) {
switch (i.customId) {
case "save": {
client.database.guilds.write(interaction.guild.id, {"verify": data} )
config = await client.database.guilds.read(interaction.guild.id);
break
}
case "switch": {
data.enabled = !data.enabled;
break
}
}
} else {
await i.deferUpdate();
break;
data.role = i.values[0]!;
}
} // TODO: On save, clear SEN
await interaction.editReply({
embeds: [new EmbedBuilder(m.embeds[0]!.data).setFooter({ text: "Message closed" })],
components: []
});
} while (!closed);
await interaction.deleteReply()
};
const check = (interaction: CommandInteraction, _partial: boolean = false) => {

@ -1,304 +1,260 @@
import { LoadingEmbed } from "../../utils/defaults.js";
import Discord, {
Channel,
CommandInteraction,
Message,
AutocompleteInteraction,
ActionRowBuilder,
ButtonBuilder,
MessageComponentInteraction,
Role,
ButtonStyle,
AutocompleteInteraction,
GuildChannel,
EmbedBuilder
APIMessageComponentEmoji,
ChannelSelectMenuBuilder,
RoleSelectMenuBuilder,
RoleSelectMenuInteraction,
ChannelSelectMenuInteraction,
ButtonInteraction,
ModalBuilder,
TextInputBuilder,
TextInputStyle,
ModalSubmitInteraction,
} from "discord.js";
import type { SlashCommandSubcommandBuilder } from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import client from "../../utils/client.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
import generateKeyValueList from "../../utils/generateKeyValueList.js";
import { ChannelType } from "discord-api-types/v9";
import getEmojiByName from "../../utils/getEmojiByName.js";
import convertCurlyBracketString from "../../utils/convertCurlyBracketString.js";
import { modalInteractionCollector } from "../../utils/dualCollector.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder
.setName("welcome")
.setDescription("Messages and roles sent or given when someone joins the server")
.addStringOption((option) =>
option
.setName("message")
.setDescription("The message to send when someone joins the server")
.setAutocomplete(true)
)
.addRoleOption((option) =>
option.setName("role").setDescription("The role given when someone joins the server")
)
.addRoleOption((option) =>
option.setName("ping").setDescription("The role pinged when someone joins the server")
)
.addChannelOption((option) =>
option
.setName("channel")
.setDescription("The channel the welcome message should be sent to")
.addChannelTypes(ChannelType.GuildText)
);
const callback = async (interaction: CommandInteraction): Promise<unknown> => {
const { renderRole, renderChannel, log, NucleusColors, entry, renderUser } = client.logger;
await interaction.reply({
const callback = async (interaction: CommandInteraction): Promise<void> => {
const { renderChannel } = client.logger;
const m = await interaction.reply({
embeds: LoadingEmbed,
fetchReply: true,
ephemeral: true
});
let m: Message;
if (
interaction.options.get("role")?.role ||
interaction.options.get("channel")?.channel ||
interaction.options.get("message")?.value as string
) {
let role: Role | null;
let ping: Role | null;
let channel: Channel | null;
const message: string | null = interaction.options.get("message")?.value as string | null;
try {
role = interaction.options.get("role")?.role as Role | null;
ping = interaction.options.get("ping")?.role as Role | null;
} catch {
return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("GUILD.ROLES.DELETE")
.setTitle("Welcome Events")
.setDescription("The role you provided is not a valid role")
.setStatus("Danger")
]
});
}
try {
channel = interaction.options.get("channel")?.channel as Channel | null;
} catch {
return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("GUILD.ROLES.DELETE")
.setTitle("Welcome Events")
.setDescription("The channel you provided is not a valid channel")
.setStatus("Danger")
]
});
}
const options: {
role?: string;
ping?: string;
channel?: string;
message?: string;
} = {};
if (role) options.role = renderRole(role);
if (ping) options.ping = renderRole(ping);
if (channel) options.channel = renderChannel(channel as GuildChannel);
if (message) options.message = "\n> " + message;
const confirmation = await new confirmationMessage(interaction)
.setEmoji("GUILD.ROLES.EDIT")
.setTitle("Welcome Events")
.setDescription(generateKeyValueList(options))
.setColor("Warning")
.setFailedMessage("No changes were made", "Success", "GUILD.ROLES.CREATE")
.setInverted(true)
.send(true);
if (confirmation.cancelled) return;
if (confirmation.success) {
try {
const toChange: {
"welcome.role"?: string;
"welcome.ping"?: string;
"welcome.channel"?: string;
"welcome.message"?: string;
} = {};
if (role) toChange["welcome.role"] = role.id;
if (ping) toChange["welcome.ping"] = ping.id;
if (channel) toChange["welcome.channel"] = channel.id;
if (message) toChange["welcome.message"] = message;
await client.database.guilds.write(interaction.guild!.id, toChange);
const list: {
memberId: ReturnType<typeof entry>;
changedBy: ReturnType<typeof entry>;
role?: ReturnType<typeof entry>;
ping?: ReturnType<typeof entry>;
channel?: ReturnType<typeof entry>;
message?: ReturnType<typeof entry>;
} = {
memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
changedBy: entry(interaction.user.id, renderUser(interaction.user))
};
if (role) list.role = entry(role.id, renderRole(role));
if (ping) list.ping = entry(ping.id, renderRole(ping));
if (channel) list.channel = entry(channel.id, renderChannel(channel as GuildChannel));
if (message) list.message = entry(message, `\`${message}\``);
const data = {
meta: {
type: "welcomeSettingsUpdated",
displayName: "Welcome Settings Changed",
calculateType: "nucleusSettingsUpdated",
color: NucleusColors.green,
emoji: "CONTROL.BLOCKTICK",
timestamp: new Date().getTime()
},
list: list,
hidden: {
guild: interaction.guild!.id
}
};
log(data);
} catch (e) {
console.log(e);
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Welcome Events")
.setDescription("Something went wrong while updating welcome settings")
.setStatus("Danger")
.setEmoji("GUILD.ROLES.DELETE")
],
components: []
});
}
} else {
return interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Welcome Events")
.setDescription("No changes were made")
.setStatus("Success")
.setEmoji("GUILD.ROLES.CREATE")
],
components: []
});
}
}
let lastClicked = null;
let timedOut = false;
let closed = false;
let config = await client.database.guilds.read(interaction.guild!.id);
let data = Object.assign({}, config.welcome);
do {
const config = await client.database.guilds.read(interaction.guild!.id);
m = (await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Welcome Events")
.setDescription(
`**Message:** ${config.welcome.message ? `\n> ${config.welcome.message}` : "*None set*"}\n` +
`**Role:** ${
config.welcome.role
? renderRole((await interaction.guild!.roles.fetch(config.welcome.role))!)
: "*None set*"
}\n` +
`**Ping:** ${
config.welcome.ping
? renderRole((await interaction.guild!.roles.fetch(config.welcome.ping))!)
: "*None set*"
}\n` +
`**Channel:** ${
config.welcome.channel
? config.welcome.channel === "dm"
? "DM"
: renderChannel((await interaction.guild!.channels.fetch(config.welcome.channel))!)
: "*None set*"
}`
const buttons = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("switch")
.setLabel(data.enabled ? "Enabled" : "Disabled")
.setStyle(data.enabled ? ButtonStyle.Success : ButtonStyle.Danger)
.setEmoji(getEmojiByName(data.enabled ? "CONTROL.TICK" : "CONTROL.CROSS", "id") as APIMessageComponentEmoji),
new ButtonBuilder()
.setCustomId("message")
.setLabel((data.message ? "Change" : "Set") + "Message")
.setStyle(ButtonStyle.Primary)
.setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
new ButtonBuilder()
.setCustomId("channelDM")
.setLabel("Send in DMs")
.setStyle(ButtonStyle.Primary)
.setDisabled(data.channel === "dm"),
new ButtonBuilder()
.setCustomId("role")
.setLabel("Clear Role")
.setStyle(ButtonStyle.Danger)
.setEmoji(getEmojiByName("CONTROL.CROSS", "id") as APIMessageComponentEmoji),
new ButtonBuilder()
.setCustomId("save")
.setLabel("Save")
.setStyle(ButtonStyle.Success)
.setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji)
.setDisabled(
data.enabled === config.welcome.enabled &&
data.message === config.welcome.message &&
data.role === config.welcome.role &&
data.ping === config.welcome.ping &&
data.channel === config.welcome.channel
)
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setLabel(lastClicked === "clear-message" ? "Click again to confirm" : "Clear Message")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
.setCustomId("clear-message")
.setDisabled(!config.welcome.message)
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setLabel(lastClicked === "clear-role" ? "Click again to confirm" : "Clear Role")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
.setCustomId("clear-role")
.setDisabled(!config.welcome.role)
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setLabel(lastClicked === "clear-ping" ? "Click again to confirm" : "Clear Ping")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
.setCustomId("clear-ping")
.setDisabled(!config.welcome.ping)
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setLabel(lastClicked === "clear-channel" ? "Click again to confirm" : "Clear Channel")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
.setCustomId("clear-channel")
.setDisabled(!config.welcome.channel)
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setLabel("Set Channel to DM")
.setCustomId("set-channel-dm")
.setDisabled(config.welcome.channel === "dm")
.setStyle(ButtonStyle.Secondary)
])
]
})) as Message;
let i: MessageComponentInteraction;
);
const channelMenu = new ActionRowBuilder<ChannelSelectMenuBuilder>()
.addComponents(
new ChannelSelectMenuBuilder()
.setCustomId("channel")
.setPlaceholder("Select a channel to send welcome messages to")
);
const roleMenu = new ActionRowBuilder<RoleSelectMenuBuilder>()
.addComponents(
new RoleSelectMenuBuilder()
.setCustomId("roleToGive")
.setPlaceholder("Select a role to give to the member when they join the server")
);
const pingMenu = new ActionRowBuilder<RoleSelectMenuBuilder>()
.addComponents(
new RoleSelectMenuBuilder()
.setCustomId("roleToPing")
.setPlaceholder("Select a role to ping when a member joins the server")
);
const embed = new EmojiEmbed()
.setTitle("Welcome Settings")
.setStatus("Success")
.setDescription(
`${getEmojiByName(data.enabled ? "CONTROL.TICK" : "CONTROL.CROSS")} Welcome messages and roles are ${data.enabled ? "enabled" : "disabled"}\n` +
`**Welcome message:** ${data.message ?
`\n> ` +
await convertCurlyBracketString(
data.message,
interaction.user.id,
interaction.user.username,
interaction.guild!.name,
interaction.guild!.members
)
: "*None*"}\n` +
`**Send message in:** ` + (data.channel ? (data.channel == "dm" ? "DMs" : renderChannel(data.channel)) : `*None set*`) + `\n` +
`**Role to ping:** ` + (data.ping ? `<@&${data.ping}>` : `*None set*`) + `\n` +
`**Role given on join:** ` + (data.role ? `<@&${data.role}>` : `*None set*`)
)
await interaction.editReply({
embeds: [embed],
components: [buttons, channelMenu, roleMenu, pingMenu]
});
let i: RoleSelectMenuInteraction | ChannelSelectMenuInteraction | ButtonInteraction;
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 }
});
filter: (interaction) => interaction.user.id === interaction.user.id,
time: 300000
}) as RoleSelectMenuInteraction | ChannelSelectMenuInteraction | ButtonInteraction;
} catch (e) {
timedOut = true;
closed = true;
continue;
}
await i.deferUpdate();
if (i.customId === "clear-message") {
if (lastClicked === "clear-message") {
await client.database.guilds.write(interaction.guild!.id, {
"welcome.message": null
});
lastClicked = null;
} else {
lastClicked = "clear-message";
}
} else if (i.customId === "clear-role") {
if (lastClicked === "clear-role") {
await client.database.guilds.write(interaction.guild!.id, {
"welcome.role": null
});
lastClicked = null;
} else {
lastClicked = "clear-role";
}
} else if (i.customId === "clear-ping") {
if (lastClicked === "clear-ping") {
await client.database.guilds.write(interaction.guild!.id, {
"welcome.ping": null
});
lastClicked = null;
} else {
lastClicked = "clear-ping";
if(i.isButton()) {
switch(i.customId) {
case "switch": {
await i.deferUpdate();
data.enabled = !data.enabled;
break;
}
case "message": {
const modal = new ModalBuilder()
.setCustomId("modal")
.setTitle("Welcome Message")
.addComponents(
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("ex1")
.setLabel("Server Info (1/3)")
.setPlaceholder(
`{serverName} - This server's name\n\n` +
`These placeholders will be replaced with the server's name, etc..`
)
.setMaxLength(1)
.setRequired(false)
.setStyle(TextInputStyle.Paragraph)
),
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("ex2")
.setLabel("Member Counts (2/3) - {MemberCount:...}")
.setPlaceholder(
`{:all} - Total member count\n` +
`{:humans} - Total non-bot users\n` +
`{:bots} - Number of bots\n`
)
.setMaxLength(1)
.setRequired(false)
.setStyle(TextInputStyle.Paragraph)
),
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("ex3")
.setLabel("Member who joined (3/3) - {member:...}")
.setPlaceholder(
`{:name} - The members name\n`
)
.setMaxLength(1)
.setRequired(false)
.setStyle(TextInputStyle.Paragraph)
),
new ActionRowBuilder<TextInputBuilder>()
.addComponents(
new TextInputBuilder()
.setCustomId("message")
.setPlaceholder("Enter a message to send when someone joins the server")
.setValue(data.message ?? "")
.setLabel("Message")
.setStyle(TextInputStyle.Paragraph)
)
)
const button = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("back")
.setLabel("Back")
.setStyle(ButtonStyle.Secondary)
.setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
)
await i.showModal(modal)
await i.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Welcome Settings")
.setDescription("Modal opened. If you can't see it, click back and try again.")
.setStatus("Success")
],
components: [button]
});
let out: ModalSubmitInteraction | null;
try {
out = await modalInteractionCollector(m, interaction.user) as ModalSubmitInteraction | null;
} catch (e) {
console.error(e);
out = null;
}
if(!out) break;
data.message = out.fields.getTextInputValue("message") ?? null;
break;
}
case "save": {
await i.deferUpdate();
await client.database.guilds.write(interaction.guild!.id, {"welcome": data});
config = await client.database.guilds.read(interaction.guild!.id);
data = Object.assign({}, config.welcome);
break;
}
case "channelDM": {
await i.deferUpdate();
data.channel = "dm";
break;
}
case "role": {
await i.deferUpdate();
data.role = null;
break;
}
}
} else if (i.customId === "clear-channel") {
if (lastClicked === "clear-channel") {
await client.database.guilds.write(interaction.guild!.id, {
"welcome.channel": null
});
lastClicked = null;
} else {
lastClicked = "clear-channel";
} else if (i.isRoleSelectMenu()) {
await i.deferUpdate();
switch(i.customId) {
case "roleToGive": {
data.role = i.values[0]!;
break
}
case "roleToPing": {
data.ping = i.values[0]!;
break
}
}
} else if (i.customId === "set-channel-dm") {
await client.database.guilds.write(interaction.guild!.id, {
"welcome.channel": "dm"
});
lastClicked = null;
} else {
await i.deferUpdate();
data.channel = i.values[0]!;
}
} while (!timedOut);
await interaction.editReply({
embeds: [new EmbedBuilder(m.embeds[0]!.data).setFooter({ text: "Message timed out" })],
components: []
});
} while (!closed);
await interaction.deleteReply()
};
const check = (interaction: CommandInteraction, _partial: boolean = false) => {

@ -33,7 +33,7 @@ export async function callback(_client: NucleusClient, member: GuildMember) {
try {
await channel.send({
embeds: [new EmojiEmbed().setDescription(string).setStatus("Success")],
content: (config.welcome.ping ? `<@${config.welcome.ping}>` : "") + `<@${member.id}>`
content: (config.welcome.ping ? `<@&${config.welcome.ping}>` : "") + `<@${member.id}>`
});
} catch (err) {
singleNotify(

@ -17,16 +17,14 @@ const logs = [
"webhookUpdate",
"guildMemberVerify",
"autoModeratorDeleted",
"nucleusSettingsUpdated",
"ticketUpdate"
"ticketUpdate",
// "nucleusSettingsUpdated"
];
const tickets = ["support", "report", "question", "issue", "suggestion", "other"];
const toHexInteger = (permissions: string[], array?: string[]): string => {
if (!array) {
array = logs;
}
if (!array) { array = logs; }
let int = 0n;
for (const perm of permissions) {

@ -56,7 +56,7 @@ async function registerCommands() {
}
}
console.log(`${colours.green}Processed ${processed.length} commands`)
console.log(`${colours.green}Processed ${processed.length} commands${colours.none}`)
return processed;
};

@ -1,11 +1,9 @@
import { TextInputBuilder } from "discord.js";
import Discord, {
CommandInteraction,
Interaction,
Message,
ActionRowBuilder,
ButtonBuilder,
MessageComponentInteraction,
ModalSubmitInteraction,
ButtonStyle,
TextInputStyle
@ -246,12 +244,7 @@ class confirmationMessage {
});
let out;
try {
out = await modalInteractionCollector(
m,
(m: Interaction) =>
(m as MessageComponentInteraction | ModalSubmitInteraction).channelId === this.interaction.channelId,
(m) => m.customId === "reason"
);
out = await modalInteractionCollector(m, this.interaction.user) as Discord.ModalSubmitInteraction | null;
} catch (e) {
cancelled = true;
continue;

@ -1,4 +1,4 @@
import { ButtonInteraction, Client, Interaction, InteractionCollector, Message, MessageComponentInteraction, ModalSubmitInteraction } from "discord.js";
import { ButtonInteraction, Client, User, Interaction, InteractionCollector, Message, MessageComponentInteraction, ModalSubmitInteraction } from "discord.js";
import client from "./client.js";
export default async function (
@ -45,17 +45,25 @@ export default async function (
return out;
}
function defaultInteractionFilter(i: MessageComponentInteraction, user: User, m: Message) {
return i.channel!.id === m.channel!.id && i.user.id === user.id
}
function defaultModalFilter(i: ModalSubmitInteraction, user: User, m: Message) {
return i.channel!.id === m.channel!.id && i.user.id === user.id
}
export async function modalInteractionCollector(
m: Message,
modalFilter: (i: Interaction) => boolean | Promise<boolean>,
interactionFilter: (i: MessageComponentInteraction) => boolean | Promise<boolean>
m: Message, user: User,
modalFilter?: (i: Interaction) => boolean | Promise<boolean>,
interactionFilter?: (i: MessageComponentInteraction) => boolean | Promise<boolean>
): Promise<null | ButtonInteraction | ModalSubmitInteraction> {
let out: ButtonInteraction | ModalSubmitInteraction;
try {
out = await new Promise((resolve, _reject) => {
const int = m
.createMessageComponentCollector({
filter: (i: MessageComponentInteraction) => interactionFilter(i),
filter: (i: MessageComponentInteraction) => (interactionFilter ? interactionFilter(i) : true) && defaultInteractionFilter(i, user, m),
time: 300000
})
.on("collect", async (i: ButtonInteraction) => {
@ -65,7 +73,7 @@ export async function modalInteractionCollector(
resolve(i);
});
const mod = new InteractionCollector(client as Client, {
filter: (i: Interaction) => modalFilter(i) && i.isModalSubmit(),
filter: (i: Interaction) => (modalFilter ? modalFilter(i) : true) && i.isModalSubmit() && defaultModalFilter(i, user, m),
time: 300000
}).on("collect", async (i: ModalSubmitInteraction) => {
int.stop();
@ -75,6 +83,7 @@ export async function modalInteractionCollector(
});
});
} catch (e) {
console.log(e);
return null;
}
return out;

Loading…
Cancel
Save