worked on settings/rolemenu and help

pull/11/head
TheCodedProf 3 years ago
parent fc420b79d0
commit 1c3ad3ce4b

14
TODO

@ -1,16 +1,4 @@
Role all
? Role all
Server rules
verificationRequired on welcome
// TODO !IMPORTANT! URL + image hash + file hash database
ROLE MENU SETTINGS
**Title**
> Description
name: role
name: role
name: role
[ Select an option to remove ]
[ Reorder roles ]
< Previous page | Add role | Delete page | Add page >

@ -22,6 +22,21 @@
"roles": true
}
},
"roleMenu": [],
"tracks": []
"tracks": [],
"rolemenu": {
"enabled": false,
"allowWebUI": false,
"options": [
{
"name": false,
"description": false,
"options": [
{
"name": false,
"description": false
}
]
}
]
}
}

@ -30,6 +30,36 @@ export interface RoleMenuSchema {
interaction: CommandInteraction | ButtonInteraction | ContextMenuCommandInteraction;
}
interface ObjectSchema {
name: string;
description: string;
min: number;
max: number;
options: {
name: string;
description: string | null;
role: string;
}[];
}
export const configToDropdown = (placeholder: string, currentPageData: ObjectSchema, selectedRoles?: string[]): ActionRowBuilder<StringSelectMenuBuilder> => {
return new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
new StringSelectMenuBuilder()
.setCustomId("roles")
.setPlaceholder(placeholder)
.setMinValues(currentPageData.min)
.setMaxValues(currentPageData.max)
.addOptions(currentPageData.options.map((option: {name: string; description: string | null; role: string;}) => {
const builder = new StringSelectMenuOptionBuilder()
.setLabel(option.name)
.setValue(option.role)
.setDefault(selectedRoles ? selectedRoles.includes(option.role) : false);
if (option.description) builder.setDescription(option.description);
return builder;
}))
)
}
export async function callback(interaction: CommandInteraction | ButtonInteraction) {
if (!interaction.member) return;
if (!interaction.guild) return;
@ -56,7 +86,7 @@ export async function callback(interaction: CommandInteraction | ButtonInteracti
],
ephemeral: true
});
const m = await interaction.reply({ embeds: LoadingEmbed, ephemeral: true });
const m = await interaction.reply({ embeds: LoadingEmbed, ephemeral: true, fetchReply: true });
if (config.roleMenu.allowWebUI) { // TODO: Make rolemenu web ui
const loginMethods: {webUI: boolean} = {
webUI: false
@ -124,9 +154,10 @@ export async function callback(interaction: CommandInteraction | ButtonInteracti
try {
component = 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 && i.channelId === interaction.channelId && i.message.id === m.id}
});
} catch (e) {
console.log(e);
return;
}
component.deferUpdate();
@ -175,21 +206,7 @@ export async function callback(interaction: CommandInteraction | ButtonInteracti
.setCustomId("done")
.setDisabled(!complete)
),
new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
new StringSelectMenuBuilder()
.setCustomId("roles")
.setPlaceholder("Select...")
.setMinValues(currentPageData.min)
.setMaxValues(currentPageData.max)
.addOptions(currentPageData.options.map((option) => {
const builder = new StringSelectMenuOptionBuilder()
.setLabel(option.name)
.setValue(option.role)
.setDefault(selectedRoles[page]!.includes(option.role));
if (option.description) builder.setDescription(option.description);
return builder;
}))
)
configToDropdown("Select...", currentPageData, selectedRoles[page])
];
await interaction.editReply({
embeds: [embed],
@ -202,6 +219,7 @@ export async function callback(interaction: CommandInteraction | ButtonInteracti
filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id && i.message.id === m.id}
});
} catch (e) {
console.log(e);
return;
}
component.deferUpdate();

@ -1,17 +1,89 @@
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction } from "discord.js";
import { ActionRowBuilder, CommandInteraction, StringSelectMenuBuilder, ApplicationCommand, ApplicationCommandOptionType } from "discord.js";
import { SlashCommandBuilder } from "@discordjs/builders";
import client from "../utils/client.js";
import EmojiEmbed from "../utils/generateEmojiEmbed.js";
import { LoadingEmbed } from "../utils/defaults.js";
const command = new SlashCommandBuilder()
.setName("help")
.setDescription("Shows help for commands");
const callback = async (interaction: CommandInteraction): Promise<void> => {
interaction.reply({components: [new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("Create ticket")
.setStyle(ButtonStyle.Primary)
.setCustomId("createticket")
)]}); // TODO: FINISH THIS FOR RELEASE
const m = await interaction.reply({ embeds: LoadingEmbed, fetchReply: true });
const commands = client.fetchedCommands;
const commandRow = new ActionRowBuilder<StringSelectMenuBuilder>()
.addComponents(
commands.map((command) => {
return new StringSelectMenuBuilder()
.setCustomId(command.name)
.setPlaceholder("Select a command")
.addOptions({
label: command.name,
value: command.name
})
})
);
let closed = false;
do {
let current: ApplicationCommand | undefined;
const subcommandGroupRow = new ActionRowBuilder<StringSelectMenuBuilder>()
.addComponents(
new StringSelectMenuBuilder()
.setCustomId("subcommandGroupRow")
);
const subcommandRow = new ActionRowBuilder<StringSelectMenuBuilder>()
.addComponents(
new StringSelectMenuBuilder()
.setCustomId("subcommandRow")
);
const embed = new EmojiEmbed()
.setTitle("Help")
.setStatus("Success")
.setEmoji("📖")
if(!current) {
embed.setDescription(
`**${"Help Menu Home"}**\n\n` +
`${"Select a command to get started"}`
)
} else {
embed.setDescription(
`**${current.name}**\n\n` +
`${current.description}`
)
if(current.options.filter((option) => option.type === ApplicationCommandOptionType.SubcommandGroup).length > 0) {
subcommandGroupRow.components[0]!
.addOptions(
current.options.filter((option) => option.type === ApplicationCommandOptionType.SubcommandGroup).map((option) => {
return {
label: option.name,
value: option.name
}
})
)
} else {
if(current.options.filter((option) => option.type === ApplicationCommandOptionType.Subcommand).length > 0) {
subcommandRow.components[0]!
.addOptions(
current.options.filter((option) => option.type === ApplicationCommandOptionType.Subcommand).map((option) => {
return {
label: option.name,
value: option.name
}
})
)
}
}
}
let cmps = [commandRow];
if(subcommandGroupRow.components[0]!.options.length > 0) cmps.push(subcommandGroupRow);
if(subcommandRow.components[0]!.options.length > 0) cmps.push(subcommandRow);
await interaction.editReply({ embeds: [embed], components: cmps });
} while (!closed);
};
const check = () => {

@ -91,7 +91,7 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
.setDescription(
`You have already activated premium on the maximum amount of servers!` + firstDescription
)
.setEmoji("NUCLEUS.LOGO")
.setEmoji("NUCLEUS.PREMIUMACTIVATE")
.setStatus("Danger")
],
components: []

@ -1,291 +0,0 @@
import { LoadingEmbed } from "../../utils/defaults.js";
import Discord, { CommandInteraction, Message, ActionRowBuilder, StringSelectMenuBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuOptionBuilder, APIMessageComponentEmoji, MessageComponentInteraction, TextInputBuilder } from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import client from "../../utils/client.js";
import convertCurlyBracketString from "../../utils/convertCurlyBracketString.js";
import singleNotify from "../../utils/singleNotify.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import createPageIndicator from "../../utils/createPageIndicator.js";
import { modalInteractionCollector } from "../../utils/dualCollector.js";
import type { GuildConfig } from "../../utils/database.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder
.setName("oldstats")
.setDescription("Controls channels which update when someone joins or leaves the server")
type ChangesType = Record<string, { name?: string; enabled?: boolean; }>
const applyChanges = (baseObject: GuildConfig['stats'], changes: ChangesType): GuildConfig['stats'] => {
for (const [id, { name, enabled }] of Object.entries(changes)) {
if (!baseObject[id]) baseObject[id] = { name: "", enabled: false};
if (name) baseObject[id]!.name = name;
if (enabled) baseObject[id]!.enabled = enabled;
}
return baseObject;
}
const callback = async (interaction: CommandInteraction) => {
try{
if (!interaction.guild) return;
const { renderChannel } = client.logger;
let closed = false;
let page = 0;
const m: Message = await interaction.reply({ embeds: LoadingEmbed, ephemeral: true, fetchReply: true });
let changes: ChangesType = {};
do {
const config = await client.database.guilds.read(interaction.guild.id);
const stats = config.stats;
let currentID = "";
let current: {
name: string;
enabled: boolean;
} = {
name: "",
enabled: false
};
let description = "";
const pageSelect = new StringSelectMenuBuilder()
.setCustomId("page")
.setPlaceholder("Select a stats channel to manage")
.setDisabled(Object.keys(stats).length === 0)
.setMinValues(1)
.setMaxValues(1);
const actionSelect = new StringSelectMenuBuilder()
.setCustomId("action")
.setPlaceholder("Perform an action")
.setMinValues(1)
.setMaxValues(1)
.setDisabled(Object.keys(stats).length === 0)
.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel("Edit")
.setValue("edit")
.setDescription("Edit the name of this stats channel")
.setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
new StringSelectMenuOptionBuilder()
.setLabel("Delete")
.setValue("delete")
.setDescription("Delete this stats channel")
.setEmoji(getEmojiByName("TICKETS.ISSUE", "id") as APIMessageComponentEmoji)
);
if (Object.keys(stats).length === 0) {
description = "You do not have any stats channels set up yet"
pageSelect.addOptions(new StringSelectMenuOptionBuilder().setLabel("No stats channels").setValue("none"))
} else {
currentID = Object.keys(stats)[page]!
current = stats[currentID]!;
current = applyChanges({ [currentID]: current }, changes)[currentID]!;
for (const [id, { name, enabled }] of Object.entries(stats)) {
pageSelect.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel(name)
.setValue(id)
.setDescription(`Enabled: ${enabled}`)
);
}
actionSelect.addOptions(new StringSelectMenuOptionBuilder()
.setLabel(current.enabled ? "Disable" : "Enable")
.setValue("toggleEnabled")
.setDescription(`Currently ${current.enabled ? "Enabled" : "Disabled"}, click to ${current.enabled ? "disable" : "enable"} this channel`)
.setEmoji(getEmojiByName(current.enabled ? "CONTROL.TICK" : "CONTROL.CROSS", "id") as APIMessageComponentEmoji)
);
description = `**Currently Editing:** ${renderChannel(currentID)}\n\n` +
`${getEmojiByName(current.enabled ? "CONTROL.TICK" : "CONTROL.CROSS")} Currently ${current.enabled ? "Enabled" : "Disabled"}\n` +
`**Name:** \`${current.name}\`\n` +
`**Preview:** ${await convertCurlyBracketString(current.name, interaction.user.id, interaction.user.username, interaction.guild.name, interaction.guild.members)}`
}
const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("back")
.setStyle(ButtonStyle.Primary)
.setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
.setDisabled(page === 0),
new ButtonBuilder()
.setCustomId("next")
.setEmoji(getEmojiByName("CONTROL.RIGHT", "id") as APIMessageComponentEmoji)
.setStyle(ButtonStyle.Primary)
.setDisabled(page === Object.keys(stats).length - 1),
new ButtonBuilder()
.setCustomId("add")
.setLabel("Create new")
.setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id") as APIMessageComponentEmoji)
.setStyle(ButtonStyle.Secondary)
.setDisabled(Object.keys(stats).length >= 24),
new ButtonBuilder()
.setCustomId("save")
.setLabel("Save")
.setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji)
.setStyle(ButtonStyle.Success)
.setDisabled(Object.keys(changes).length === 0),
);
const embed = new EmojiEmbed()
.setTitle("Stats Channels")
.setDescription(description + "\n\n" + createPageIndicator(Object.keys(stats).length, page))
.setEmoji("SETTINGS.STATS.GREEN")
.setStatus("Success")
interaction.editReply({
embeds: [embed],
components: [
new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(pageSelect),
new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(actionSelect),
row
]
});
let i: MessageComponentInteraction;
try {
i = await m.awaitMessageComponent({ filter: (i) => i.user.id === interaction.user.id && i.message.id === m.id, time: 30000 });
} catch (e) {
closed = true;
continue;
}
if (i.isStringSelectMenu()) {
switch(i.customId) {
case "page":
page = Object.keys(stats).indexOf(i.values[0]!);
await i.deferUpdate();
break;
case "action":
if(!changes[currentID]) changes[currentID] = {};
switch(i.values[0]!) {
case "edit":
await i.showModal(
new Discord.ModalBuilder()
.setCustomId("modal")
.setTitle(`Stats channel name`)
.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(Discord.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(Discord.TextInputStyle.Paragraph)
),
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("ex3")
.setLabel("Latest Member (3/3) - {member:...}")
.setPlaceholder(
`{:name} - The members name\n`
)
.setMaxLength(1)
.setRequired(false)
.setStyle(Discord.TextInputStyle.Paragraph)
),
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("text")
.setLabel("Channel name input")
.setMaxLength(1000)
.setRequired(true)
.setStyle(Discord.TextInputStyle.Short)
.setValue(current.name)
)
)
);
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Stats Channel")
.setDescription("Modal opened. If you can't see it, click back and try again.")
.setStatus("Success")
.setEmoji("SETTINGS.STATS.GREEN")
],
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("Back")
.setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
.setStyle(ButtonStyle.Primary)
.setCustomId("back")
)
]
});
let out: Discord.ModalSubmitInteraction | null;
try {
out = await modalInteractionCollector(
m,
(m) => m.channel!.id === interaction.channel!.id,
(_) => true
) as Discord.ModalSubmitInteraction | null;
} catch (e) {
continue;
}
if (!out) continue
if (!out.fields) continue
if (out.isButton()) continue;
const newString = out.fields.getTextInputValue("text");
if (!newString) continue;
changes[currentID]!.name = newString;
break;
case "delete":
changes[currentID] = {};
await i.deferUpdate();
break;
case "toggleEnabled":
changes[currentID]!.enabled = !stats[currentID]!.enabled;
await i.deferUpdate();
break;
}
break;
}
} else if (i.isButton()) {
await i.deferUpdate();
switch(i.customId) {
case "back":
page--;
break;
case "next":
page++;
break;
case "add":
break;
case "save":
const changed = applyChanges(config.stats, changes);
singleNotify("statsChannelDeleted", interaction.guild.id, true)
config.stats = changed;
changes = {}
await client.database.guilds.write(interaction.guildId!, config);
}
}
console.log(changes, config.stats);
} while (!closed);
} catch(e) {
console.log(e)
}
};
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
if (!member.permissions.has("ManageChannels"))
return "You must have the *Manage Channels* permission to use this command";
return true;
};
export { command };
export { callback };
export { check };

@ -1,16 +1,350 @@
import type Discord from "discord.js";
import type { CommandInteraction } from "discord.js";
import { ActionRowBuilder, APIMessageComponentEmoji, ButtonBuilder, ButtonInteraction, ButtonStyle, CommandInteraction, Message, ModalBuilder, RoleSelectMenuBuilder, RoleSelectMenuInteraction, StringSelectMenuBuilder, StringSelectMenuInteraction, StringSelectMenuOptionBuilder, TextInputBuilder, TextInputStyle } from "discord.js";
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import { LoadingEmbed } from "../../utils/defaults.js";
import client from "../../utils/client.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import createPageIndicator from "../../utils/createPageIndicator.js";
import { configToDropdown } from "../../actions/roleMenu.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder
.setName("rolemenu")
.setDescription("rolemenu") // TODO
.addRoleOption((option) => option.setName("role").setDescription("The role to give after verifying")); // FIXME FOR FUCK SAKE
.setDescription("rolemenu")
interface ObjectSchema {
name: string;
description: string;
min: number;
max: number;
options: {
name: string;
description: string | null;
role: string;
}[];
}
const editNameDescription = async (interaction: StringSelectMenuInteraction | ButtonInteraction, m: Message, data: {name?: string, description?: string}) => {
let {name, description} = data;
const modal = new ModalBuilder()
.setTitle("Edit Name and Description")
.setCustomId("editNameDescription")
.addComponents(
new ActionRowBuilder<TextInputBuilder>()
.addComponents(
new TextInputBuilder()
.setCustomId("name")
.setPlaceholder(name ?? "")
.setStyle(TextInputStyle.Short)
.setRequired(true),
new TextInputBuilder()
.setCustomId("description")
.setPlaceholder(description ?? "")
.setStyle(TextInputStyle.Short)
)
)
const button = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("back")
.setLabel("Back")
.setStyle(ButtonStyle.Secondary)
.setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
)
return [name, description]
}
const ellipsis = (str: string, max: number): string => {
if (str.length <= max) return str;
return str.slice(0, max - 3) + "...";
}
const createRoleMenuPage = async (interaction: StringSelectMenuInteraction | ButtonInteraction, m: Message, data?: ObjectSchema) => {
if (!data) data = {
name: "Role Menu Page",
description: "A new role menu page",
min: 0,
max: 0,
options: []
};
const buttons = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("back")
.setLabel("Back")
.setStyle(ButtonStyle.Secondary)
.setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
new ButtonBuilder()
.setCustomId("edit")
.setLabel("Edit")
.setStyle(ButtonStyle.Primary)
.setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
new ButtonBuilder()
.setCustomId("addRole")
.setLabel("Add Role")
.setStyle(ButtonStyle.Secondary)
.setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id") as APIMessageComponentEmoji)
);
let back = false
do {
const previewSelect = configToDropdown("Edit Roles", {name: data.name, description: data.description, min: 1, max: 1, options: data.options});
const embed = new EmojiEmbed()
.setTitle(`${data.name}`)
.setStatus("Success")
.setDescription(
`**Description:**\n> ${data.description}\n\n` +
`**Min:** ${data.min}` + (data.min === 0 ? " (Members will be given a skip button)" : "") + "\n" +
`**Max:** ${data.max}\n`
)
interaction.editReply({embeds: [embed], components: [previewSelect, buttons]});
let i: StringSelectMenuInteraction | ButtonInteraction;
try {
i = await m.awaitMessageComponent({ time: 300000, filter: (i) => i.user.id === interaction.user.id && i.message.id === m.id && i.channelId === interaction.channelId}) as ButtonInteraction | StringSelectMenuInteraction;
} catch (e) {
back = true;
break;
}
if (i.isStringSelectMenu()) {
if(i.customId === "roles") {
await i.deferUpdate();
await createRoleMenuOptionPage(interaction, m, data.options.find((o) => o.role === (i as StringSelectMenuInteraction).values[0]));
}
} else if (i.isButton()) {
await i.deferUpdate();
switch (i.customId) {
case "back":
back = true;
break;
case "edit":
let [name, description] = await editNameDescription(interaction, m, data);
data.name = name ? name : data.name;
data.description = description ? description : data.description;
break;
case "addRole":
data.options.push(await createRoleMenuOptionPage(interaction, m));
break;
}
}
} while (!back);
return data;
}
const createRoleMenuOptionPage = async (interaction: StringSelectMenuInteraction | ButtonInteraction, m: Message, data?: {name: string; description: string | null; role: string}) => {
const { renderRole} = client.logger;
if (!data) data = {
name: "Role Menu Option",
description: null,
role: "No role set"
};
let back = false;
const buttons = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("back")
.setLabel("Back")
.setStyle(ButtonStyle.Secondary)
.setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji),
new ButtonBuilder()
.setCustomId("edit")
.setLabel("Edit Details")
.setStyle(ButtonStyle.Primary)
.setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji)
);
do {
const roleSelect = new RoleSelectMenuBuilder().setCustomId("role").setPlaceholder(data.role ? "Set role to" : "Set the role");
const embed = new EmojiEmbed()
.setTitle(`${data.name ?? "New Role Menu Option"}`)
.setStatus("Success")
.setDescription(
`**Description:**\n> ${data.description ?? "No description set"}\n\n` +
`**Role:** ${renderRole((await interaction.guild!.roles.fetch(data.role))!) ?? "No role set"}\n`
)
interaction.editReply({embeds: [embed], components: [new ActionRowBuilder<RoleSelectMenuBuilder>().addComponents(roleSelect), buttons]});
let i: RoleSelectMenuInteraction | ButtonInteraction;
try {
i = await m.awaitMessageComponent({ time: 300000, filter: (i) => i.user.id === interaction.user.id && i.message.id === m.id && i.channelId === interaction.channelId}) as ButtonInteraction | RoleSelectMenuInteraction;
} catch (e) {
back = true;
break;
}
if (i.isRoleSelectMenu()) {
if(i.customId === "role") {
await i.deferUpdate();
data.role = (i as RoleSelectMenuInteraction).values[0]!;
}
} else if (i.isButton()) {
await i.deferUpdate();
switch (i.customId) {
case "back":
back = true;
break;
case "edit":
await i.deferUpdate();
let [name, description] = await editNameDescription(interaction, m, data as {name: string; description: string});
data.name = name ? name : data.name;
data.description = description ? description : data.description;
break;
}
}
} while (!back);
return data;
}
const callback = async (interaction: CommandInteraction): Promise<void> => {
console.log("we changed the charger again because fuck you");
await interaction.reply("You're mum");
if (!interaction.guild) return;
const m = await interaction.reply({embeds: LoadingEmbed, ephemeral: true, fetchReply: true});
let page = 0;
let closed = false;
const config = await client.database.guilds.read(interaction.guild.id);
let currentObject: ObjectSchema[] = config.roleMenu.options;
let modified = false;
do {
const embed = new EmojiEmbed()
.setTitle("Role Menu Settings")
.setEmoji("GUILD.GREEN")
.setStatus("Success");
const noRoleMenus = currentObject.length === 0;
let current: ObjectSchema;
const pageSelect = new StringSelectMenuBuilder()
.setCustomId("page")
.setPlaceholder("Select a Role Menu page to manage");
const actionSelect = new StringSelectMenuBuilder()
.setCustomId("action")
.setPlaceholder("Perform an action")
.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel("Edit")
.setDescription("Edit this page")
.setValue("edit")
.setEmoji(getEmojiByName("ICONS.EDIT", "id") as APIMessageComponentEmoji),
new StringSelectMenuOptionBuilder()
.setLabel("Delete")
.setDescription("Delete this page")
.setValue("delete")
.setEmoji(getEmojiByName("TICKETS.ISSUE", "id") as APIMessageComponentEmoji)
);
const buttonRow = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("back")
.setStyle(ButtonStyle.Primary)
.setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
.setDisabled(page === 0),
new ButtonBuilder()
.setCustomId("next")
.setEmoji(getEmojiByName("CONTROL.RIGHT", "id") as APIMessageComponentEmoji)
.setStyle(ButtonStyle.Primary)
.setDisabled(page === Object.keys(currentObject).length - 1),
new ButtonBuilder()
.setCustomId("add")
.setLabel("New Page")
.setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id") as APIMessageComponentEmoji)
.setStyle(ButtonStyle.Secondary)
.setDisabled(Object.keys(currentObject).length >= 24),
new ButtonBuilder()
.setCustomId("reorder")
.setLabel("Reorder Pages")
.setEmoji(getEmojiByName("ICONS.SHUFFLE", "id") as APIMessageComponentEmoji)
.setStyle(ButtonStyle.Secondary)
.setDisabled(Object.keys(currentObject).length <= 1),
new ButtonBuilder()
.setCustomId("save")
.setLabel("Save")
.setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji)
.setStyle(ButtonStyle.Success)
.setDisabled(!modified),
);
if(noRoleMenus) {
embed.setDescription("No role menu page have been set up yet. Use the button below to add one.\n\n" +
createPageIndicator(1, 1, undefined, true)
);
pageSelect.setDisabled(true);
actionSelect.setDisabled(true);
pageSelect.addOptions(new StringSelectMenuOptionBuilder()
.setLabel("No role menu pages")
.setValue("none")
);
} else {
page = Math.min(page, Object.keys(currentObject).length - 1);
current = currentObject[page]!;
embed.setDescription(`**Currently Editing:** ${current.name}\n\n` +
`**Description:** \`${current.description}\`\n` +
`\n\n${createPageIndicator(Object.keys(config.roleMenu.options).length, page)}`
);
pageSelect.addOptions(
currentObject.map((key: ObjectSchema, index) => {
return new StringSelectMenuOptionBuilder()
.setLabel(ellipsis(key.name, 50))
.setDescription(ellipsis(key.description, 50))
.setValue(index.toString());
})
);
}
await interaction.editReply({embeds: [embed], components: [new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(actionSelect), new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(pageSelect), buttonRow]});
let i: StringSelectMenuInteraction | ButtonInteraction;
try {
i = await m.awaitMessageComponent({ time: 300000, filter: (i) => i.user.id === interaction.user.id && i.message.id === m.id && i.channelId === interaction.channelId}) as ButtonInteraction | StringSelectMenuInteraction;
} catch (e) {
closed = true;
break;
}
await i.deferUpdate();
if (i.isButton()) {
switch (i.customId) {
case "back":
page--;
break;
case "next":
page++;
break;
case "add":
currentObject.push(await createRoleMenuPage(i, m));
page = currentObject.length - 1;
break;
case "reorder":
break;
case "save":
client.database.guilds.write(interaction.guild.id, {"roleMenu.options": currentObject});
modified = false;
break;
}
} else if (i.isStringSelectMenu()) {
switch (i.customId) {
case "action":
switch(i.values[0]) {
case "edit":
currentObject[page] = await createRoleMenuPage(i, m, current!);
modified = true;
break;
case "delete":
currentObject.splice(page, 1);
break;
}
break;
case "page":
page = parseInt(i.values[0]!);
break;
}
}
} while (!closed)
};
const check = (interaction: CommandInteraction) => {

@ -232,29 +232,29 @@ const callback = async (interaction: CommandInteraction) => {
.setEmoji(getEmojiByName("TICKETS.ISSUE", "id") as APIMessageComponentEmoji)
);
const buttonRow = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId("back")
.setStyle(ButtonStyle.Primary)
.setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
.setDisabled(page === 0),
new ButtonBuilder()
.setCustomId("next")
.setEmoji(getEmojiByName("CONTROL.RIGHT", "id") as APIMessageComponentEmoji)
.setStyle(ButtonStyle.Primary)
.setDisabled(page === Object.keys(currentObject).length - 1),
new ButtonBuilder()
.setCustomId("add")
.setLabel("Create new")
.setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id") as APIMessageComponentEmoji)
.setStyle(ButtonStyle.Secondary)
.setDisabled(Object.keys(currentObject).length >= 24),
new ButtonBuilder()
.setCustomId("save")
.setLabel("Save")
.setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji)
.setStyle(ButtonStyle.Success)
.setDisabled(modified),
.addComponents(
new ButtonBuilder()
.setCustomId("back")
.setStyle(ButtonStyle.Primary)
.setEmoji(getEmojiByName("CONTROL.LEFT", "id") as APIMessageComponentEmoji)
.setDisabled(page === 0),
new ButtonBuilder()
.setCustomId("next")
.setEmoji(getEmojiByName("CONTROL.RIGHT", "id") as APIMessageComponentEmoji)
.setStyle(ButtonStyle.Primary)
.setDisabled(page === Object.keys(currentObject).length - 1),
new ButtonBuilder()
.setCustomId("add")
.setLabel("Create new")
.setEmoji(getEmojiByName("TICKETS.SUGGESTION", "id") as APIMessageComponentEmoji)
.setStyle(ButtonStyle.Secondary)
.setDisabled(Object.keys(currentObject).length >= 24),
new ButtonBuilder()
.setCustomId("save")
.setLabel("Save")
.setEmoji(getEmojiByName("ICONS.SAVE", "id") as APIMessageComponentEmoji)
.setStyle(ButtonStyle.Success)
.setDisabled(modified),
);
if (noStatsChannels) {
embed.setDescription("No stats channels have been set up yet. Use the button below to add one.\n\n" +
@ -275,7 +275,6 @@ const callback = async (interaction: CommandInteraction) => {
.setDescription(`Currently ${current.enabled ? "Enabled" : "Disabled"}, click to ${current.enabled ? "disable" : "enable"} this channel`)
.setEmoji(getEmojiByName(current.enabled ? "CONTROL.TICK" : "CONTROL.CROSS", "id") as APIMessageComponentEmoji)
);
embed.setDescription(`**Currently Editing:** ${renderChannel(Object.keys(currentObject)[page]!)}\n\n` +
`${getEmojiByName(current.enabled ? "CONTROL.TICK" : "CONTROL.CROSS")} Currently ${current.enabled ? "Enabled" : "Disabled"}\n` +
`**Name:** \`${current.name}\`\n` +

@ -1,6 +1,8 @@
{
"NUCLEUS": {
"LOGO": "953040840945721385",
"PREMIUMACTIVATE": "a1067536222764925068",
"PREMIUM": "1067928702027042876",
"LOADING": "a946346549271732234",
"INFO": {
"HELP": "751751467014029322",
@ -23,6 +25,7 @@
"ATTACHMENT": "997570687193587812",
"LOGGING": "999613304446144562",
"SAVE": "1065722246322200586",
"SHUFFLE": "1067913930304921690",
"NOTIFY": {
"ON": "1000726394579464232",
"OFF": "1000726363495477368"

@ -4,17 +4,15 @@ import config from "./config/main.json" assert { type: "json" };
import register from "./utils/commandRegistration/register.js";
import { record as recordPerformance } from "./utils/performanceTesting/record.js";
client.on("ready", () => {
client.on("ready", async () => {
console.log(`Logged in as ${client.user!.tag}!`);
register();
runServer(client);
client.fetchedCommands = await client.application?.commands.fetch()!;
});
process.on("unhandledRejection", (err) => { console.error(err) });
process.on("uncaughtException", (err) => { console.error(err) });
await client.login(config.enableDevelopment ? config.developmentToken : config.token)
await recordPerformance();
import { getCommandMentionByName} from "./utils/getCommandMentionByName.js";
console.log(await getCommandMentionByName("nucleus/premium"))
await recordPerformance();

@ -178,7 +178,7 @@ export default async (guild: Guild, interaction?: CommandInteraction) => {
"**Attachment logs**\n> When a message with attachments is edited or deleted, the logs will also include the images sent.\n" +
"\nPremium is not yet available. Check `/nucleus premium` for updates on features and pricing"
)
.setEmoji("NUCLEUS.COMMANDS.LOCK")
.setEmoji("NUCLEUS.PREMIUM")
.setStatus("Danger")
)
.setTitle("Premium")

@ -1,4 +1,4 @@
import Discord, { Client, Interaction, AutocompleteInteraction, GatewayIntentBits } from 'discord.js';
import Discord, { Client, Interaction, AutocompleteInteraction, GatewayIntentBits, Collection } from 'discord.js';
import { Logger } from "../utils/log.js";
import Memory from "../utils/memory.js";
import type { VerifySchema } from "../reflex/verify.js";
@ -32,7 +32,7 @@ class NucleusClient extends Client {
check: (interaction: Interaction) => Promise<boolean> | boolean,
autocomplete: (interaction: AutocompleteInteraction) => Promise<string[]>
}> = {};
fetchedCommands: Collection<string, Discord.ApplicationCommand> = new Collection();
constructor(database: typeof NucleusClient.prototype.database) {
super({ intents: [
GatewayIntentBits.Guilds,

@ -213,6 +213,7 @@ export default async function register() {
await client.application?.commands.set(commandList);
}
}
await registerCommandHandler();
await registerEvents();
console.log(`${colours.green}Registered commands, events and context menus${colours.none}`)

@ -188,8 +188,7 @@ class confirmationMessage {
});
} catch (e) {
success = false;
returnComponents = true;
continue;
break;
}
if (component.customId === "yes") {
component.deferUpdate();
@ -277,8 +276,6 @@ class confirmationMessage {
}
const returnValue: Awaited<ReturnType<typeof this.send>> = {};
if (returnComponents || success !== undefined) returnValue.components = this.customButtons;
if (success !== undefined) returnValue.success = success;
if (cancelled) {
await this.timeoutError()
returnValue.cancelled = true;
@ -294,6 +291,8 @@ class confirmationMessage {
});
return {success: false}
}
if (returnComponents || success !== undefined) returnValue.components = this.customButtons;
if (success !== undefined) returnValue.success = success;
if (newReason) returnValue.newReason = newReason;
const typedReturnValue = returnValue as {cancelled: true} |

Loading…
Cancel
Save