worked on settings/rolemenu and help
diff --git a/TODO b/TODO
index d2cd1a2..2ab95dc 100644
--- a/TODO
+++ b/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 >
diff --git a/TODO.json b/TODO.json
index 6f0b94e..19d077c 100644
--- a/TODO.json
+++ b/TODO.json
@@ -22,6 +22,21 @@
"roles": true
}
},
- "roleMenu": [],
- "tracks": []
+ "tracks": [],
+ "rolemenu": {
+ "enabled": false,
+ "allowWebUI": false,
+ "options": [
+ {
+ "name": false,
+ "description": false,
+ "options": [
+ {
+ "name": false,
+ "description": false
+ }
+ ]
+ }
+ ]
+ }
}
diff --git a/src/actions/roleMenu.ts b/src/actions/roleMenu.ts
index 732c94d..16689b7 100644
--- a/src/actions/roleMenu.ts
+++ b/src/actions/roleMenu.ts
@@ -30,6 +30,36 @@
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 @@
],
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 @@
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 @@
.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 @@
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();
diff --git a/src/commands/help.ts b/src/commands/help.ts
index 767ca46..1a0ce16 100644
--- a/src/commands/help.ts
+++ b/src/commands/help.ts
@@ -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 = () => {
diff --git a/src/commands/nucleus/premium.ts b/src/commands/nucleus/premium.ts
index 8b47ab7..e74e23c 100644
--- a/src/commands/nucleus/premium.ts
+++ b/src/commands/nucleus/premium.ts
@@ -91,7 +91,7 @@
.setDescription(
`You have already activated premium on the maximum amount of servers!` + firstDescription
)
- .setEmoji("NUCLEUS.LOGO")
+ .setEmoji("NUCLEUS.PREMIUMACTIVATE")
.setStatus("Danger")
],
components: []
diff --git a/src/commands/settings/oldStats.ts b/src/commands/settings/oldStats.ts
deleted file mode 100644
index 8f13109..0000000
--- a/src/commands/settings/oldStats.ts
+++ /dev/null
@@ -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 };
\ No newline at end of file
diff --git a/src/commands/settings/rolemenu.ts b/src/commands/settings/rolemenu.ts
index b62d962..f8fb6f4 100644
--- a/src/commands/settings/rolemenu.ts
+++ b/src/commands/settings/rolemenu.ts
@@ -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) => {
diff --git a/src/commands/settings/stats.ts b/src/commands/settings/stats.ts
index f191a34..91da382 100644
--- a/src/commands/settings/stats.ts
+++ b/src/commands/settings/stats.ts
@@ -232,29 +232,29 @@
.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 @@
.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` +
diff --git a/src/config/emojis.json b/src/config/emojis.json
index 8c45353..05a5e1d 100644
--- a/src/config/emojis.json
+++ b/src/config/emojis.json
@@ -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"
diff --git a/src/index.ts b/src/index.ts
index a88cc54..b67da33 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,17 +4,15 @@
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();
\ No newline at end of file
diff --git a/src/reflex/guide.ts b/src/reflex/guide.ts
index e78c0dc..668b56d 100644
--- a/src/reflex/guide.ts
+++ b/src/reflex/guide.ts
@@ -178,7 +178,7 @@
"**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")
diff --git a/src/utils/client.ts b/src/utils/client.ts
index 46d9f92..a199b5b 100644
--- a/src/utils/client.ts
+++ b/src/utils/client.ts
@@ -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 @@
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,
diff --git a/src/utils/commandRegistration/register.ts b/src/utils/commandRegistration/register.ts
index 4a13807..abc8c39 100644
--- a/src/utils/commandRegistration/register.ts
+++ b/src/utils/commandRegistration/register.ts
@@ -213,6 +213,7 @@
await client.application?.commands.set(commandList);
}
}
+
await registerCommandHandler();
await registerEvents();
console.log(`${colours.green}Registered commands, events and context menus${colours.none}`)
diff --git a/src/utils/confirmationMessage.ts b/src/utils/confirmationMessage.ts
index 7d54674..6dc424e 100644
--- a/src/utils/confirmationMessage.ts
+++ b/src/utils/confirmationMessage.ts
@@ -188,8 +188,7 @@
});
} catch (e) {
success = false;
- returnComponents = true;
- continue;
+ break;
}
if (component.customId === "yes") {
component.deferUpdate();
@@ -277,8 +276,6 @@
}
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 @@
});
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} |