diff --git a/src/actions/roleMenu.ts b/src/actions/roleMenu.ts index 27b5f07..9796b28 100644 --- a/src/actions/roleMenu.ts +++ b/src/actions/roleMenu.ts @@ -70,7 +70,7 @@ export async function callback(interaction: CommandInteraction | ButtonInteracti if (!interaction.member) return; if (!interaction.guild) return; const config = await client.database.guilds.read(interaction.guild.id); - if (!config.roleMenu.enabled) + if (!config.roleMenu.enabled) { return await interaction.reply({ embeds: [ new EmojiEmbed() @@ -83,6 +83,7 @@ export async function callback(interaction: CommandInteraction | ButtonInteracti ], ephemeral: true }); + } if (config.roleMenu.options.length === 0) return await interaction.reply({ embeds: [ diff --git a/src/commands/mod/purge.ts b/src/commands/mod/purge.ts index c5fe2fb..ff69079 100644 --- a/src/commands/mod/purge.ts +++ b/src/commands/mod/purge.ts @@ -102,7 +102,7 @@ const callback = async (interaction: CommandInteraction): Promise => { try { component = m.awaitMessageComponent({ filter: (i) => - i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id && i.id === m.id, + i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id, time: 300000 }); } catch (e) { diff --git a/src/commands/nucleus/invite.ts b/src/commands/nucleus/invite.ts index 6805adb..9f78cc5 100644 --- a/src/commands/nucleus/invite.ts +++ b/src/commands/nucleus/invite.ts @@ -23,7 +23,7 @@ const callback = async (interaction: CommandInteraction): Promise => { .setURL( `https://discord.com/api/oauth2/authorize?client_id=${ client.user!.id - }&permissions=295157886134&scope=bot%20applications.commands` + }&permissions=407900777662&scope=bot%20applications.commands` ) ]) ], diff --git a/src/commands/nucleus/stats.ts b/src/commands/nucleus/stats.ts index 19c0949..058695c 100644 --- a/src/commands/nucleus/stats.ts +++ b/src/commands/nucleus/stats.ts @@ -1,22 +1,182 @@ -import type { CommandInteraction } from "discord.js"; +import { ActionRowBuilder, AttachmentBuilder, ButtonBuilder, ButtonInteraction, ButtonStyle, ChannelType, CommandInteraction, ComponentType, Guild, ModalBuilder, ModalSubmitInteraction, TextInputBuilder, TextInputStyle } from "discord.js"; import type { SlashCommandSubcommandBuilder } from "discord.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import client from "../../utils/client.js"; +import config from "../../config/main.js"; const command = (builder: SlashCommandSubcommandBuilder) => builder.setName("stats").setDescription("Gets the bot's stats"); const callback = async (interaction: CommandInteraction): Promise => { - interaction.reply({ + const description = `**Servers:** ${client.guilds.cache.size}\n` + `**Ping:** \`${client.ws.ping * 2}ms\`` + const m = await interaction.reply({ embeds: [ new EmojiEmbed() .setTitle("Stats") - .setDescription(`**Servers:** ${client.guilds.cache.size}\n` + `**Ping:** \`${client.ws.ping * 2}ms\``) + .setDescription(description) .setStatus("Success") .setEmoji("SETTINGS.STATS.GREEN") ], - ephemeral: true + ephemeral: true, + fetchReply: true }); + if (config.owners.includes(interaction.user.id)) { + interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setTitle("Admin") + .setDescription(description) + .setStatus("Success") + .setEmoji("SETTINGS.STATS.GREEN") + ], components: [new ActionRowBuilder().addComponents(new ButtonBuilder().setCustomId("admin").setLabel("Admin Panel").setStyle(ButtonStyle.Primary))] + }); + + const modal = new ModalBuilder() + .addComponents( + new ActionRowBuilder() + .addComponents( + new TextInputBuilder() + .setStyle(TextInputStyle.Short) + .setLabel("Guild ID") + .setCustomId("guildID") + .setPlaceholder("Guild ID") + .setMinLength(16) + .setMaxLength(25) + ) + ) + .setTitle("Admin Panel") + .setCustomId("adminPanel") + let i1: ButtonInteraction; + const channel = await client.channels.fetch(interaction.channelId) + if(!channel || [ChannelType.GuildCategory, ChannelType.GroupDM, ChannelType.GuildStageVoice].includes(channel.type)) return; + // console.log(interaction) + if (!("awaitMessageComponent" in channel)) return; + try { + i1 = await channel!.awaitMessageComponent({ + filter: (i) => i.customId === "admin" && i.user.id === interaction.user.id, + time: 300000 + }); + } catch (e) { console.log(e); return } + await i1.showModal(modal) + let out: ModalSubmitInteraction; + try { + out = await i1.awaitModalSubmit({ + filter: (i) => i.customId === "adminPanel" && i.user.id === interaction.user.id, + time: 300000 + }) + } catch { return } + out.deferUpdate(); + const GuildID = out.fields.getTextInputValue("guildID"); + if (!client.guilds.cache.has(GuildID)) { + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setTitle("Admin") + .setDescription("Not in server") + .setStatus("Danger") + ], components: [] + }); + }; + + await interaction.editReply({ + embeds: [], + components: [ + new ActionRowBuilder().addComponents( + new ButtonBuilder().setCustomId("stats").setLabel("Stats").setStyle(ButtonStyle.Primary), + new ButtonBuilder().setCustomId("leave").setLabel("Leave").setStyle(ButtonStyle.Danger), + new ButtonBuilder().setCustomId("data").setLabel("Guild data").setStyle(ButtonStyle.Secondary), + new ButtonBuilder().setCustomId("purge").setLabel("Delete data").setStyle(ButtonStyle.Danger), + new ButtonBuilder().setCustomId("cache").setLabel("Reset cache").setStyle(ButtonStyle.Success) + ) + ]}); + let i; + try { + i = await m.awaitMessageComponent({ + filter: (i) => i.user.id === interaction.user.id, + time: 300000 + }) + } catch { return } + i.deferUpdate(); + const guild = await client.guilds.fetch(GuildID) as Guild | null; + if (!guild) { + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setTitle("Admin") + .setDescription("Not in server") + .setStatus("Danger") + ], components: [] + }); + return; + } + if (i.customId === "stats") { + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setTitle("Stats") + .setDescription( + `**Name:** ${guild.name}\n` + + `**ID:** \`${guild.id}\`\n` + + `**Owner:** ${client.users.cache.get(guild.ownerId)!.tag}\n` + + `**Member Count:** ${guild.memberCount}\n` + + `**Created:** \n` + + `**Added Nucleus:** \n` + + `**Nucleus' Perms:** https://discordapi.com/permissions.html#${guild.members.me!.permissions.valueOf()}\n` + ) + .setStatus("Success") + .setEmoji("SETTINGS.STATS.GREEN") + ] + }) + } else if (i.customId === "leave") { + await guild.leave(); + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setTitle("Left") + .setDescription(`Left ${guild.name}`) + .setStatus("Success") + .setEmoji("SETTINGS.STATS.GREEN") + ], components: [] + }) + } else if (i.customId === "data") { + // Get all the data and convert to a string + const data = await client.database.guilds.read(guild.id); + const stringified = JSON.stringify(data, null, 2); + const buffer = Buffer.from(stringified); + const attachment = new AttachmentBuilder(buffer).setName("data.json"); + await interaction.editReply({ + embeds: [ + new EmojiEmbed().setTitle("Data").setDescription(`Data for ${guild.name}`).setStatus("Success") + ], components: [], + files: [attachment] + }) + } else if (i.customId === "purge") { + await client.database.guilds.delete(GuildID); + await client.database.history.delete(GuildID); + await client.database.notes.delete(GuildID); + await client.database.transcripts.deleteAll(GuildID); + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setTitle("Purge") + .setDescription(`Deleted data for ${guild.name}`) + .setStatus("Success") + .setEmoji("SETTINGS.STATS.GREEN") + ], components: [] + }) + } else if (i.customId === "cache") { + await client.memory.forceUpdate(guild.id); + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setTitle("Cache") + .setDescription(`Reset cache for ${guild.name}`) + .setStatus("Success") + .setEmoji("SETTINGS.STATS.GREEN") + ], components: [] + }) + } + } }; export { command }; diff --git a/src/commands/settings/rolemenu.ts b/src/commands/settings/rolemenu.ts index 11c6b67..0c174f5 100644 --- a/src/commands/settings/rolemenu.ts +++ b/src/commands/settings/rolemenu.ts @@ -25,9 +25,9 @@ import createPageIndicator from "../../utils/createPageIndicator.js"; import { configToDropdown } from "../../actions/roleMenu.js"; import { modalInteractionCollector } from "../../utils/dualCollector.js"; import ellipsis from "../../utils/ellipsis.js"; -import lodash from "lodash"; +import _ from "lodash"; -const isEqual = lodash.isEqual; +const isEqual = _.isEqual; const command = (builder: SlashCommandSubcommandBuilder) => builder.setName("rolemenu").setDescription("rolemenu"); @@ -163,19 +163,20 @@ const editNameDescription = async ( return [name, description]; }; +const defaultRoleMenuData = { + name: "Role Menu Page", + description: "A new role menu page", + min: 0, + max: 0, + options: [] +}; + const editRoleMenuPage = async ( interaction: StringSelectMenuInteraction | ButtonInteraction, m: Message, data?: ObjectSchema ): Promise => { - if (!data) - data = { - name: "Role Menu Page", - description: "A new role menu page", - min: 0, - max: 0, - options: [] - }; + if (!data) data = _.cloneDeep(defaultRoleMenuData) const buttons = new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId("back") @@ -357,7 +358,7 @@ const callback = async (interaction: CommandInteraction): Promise => { let page = 0; let closed = false; const config = await client.database.guilds.read(interaction.guild.id); - let currentObject: ObjectSchema[] = config.roleMenu.options; + let currentObject: ObjectSchema[] = _.cloneDeep(config.roleMenu.options); let modified = false; do { const embed = new EmojiEmbed().setTitle("Role Menu").setEmoji("GUILD.GREEN").setStatus("Success"); @@ -392,7 +393,7 @@ const callback = async (interaction: CommandInteraction): Promise => { .setCustomId("next") .setEmoji(getEmojiByName("CONTROL.RIGHT", "id") as APIMessageComponentEmoji) .setStyle(ButtonStyle.Primary) - .setDisabled(page === Object.keys(currentObject).length - 1), + .setDisabled(page === Object.keys(currentObject).length - 1 || noRoleMenus), new ButtonBuilder() .setCustomId("add") .setLabel("New Page") @@ -472,7 +473,7 @@ const callback = async (interaction: CommandInteraction): Promise => { } case "add": { const newPage = await editRoleMenuPage(i, m); - if (!newPage) break; + if (_.isEqual(newPage, defaultRoleMenuData)) break; currentObject.push(); page = currentObject.length - 1; break; diff --git a/src/commands/settings/tracks.ts b/src/commands/settings/tracks.ts index 5215b3f..625bcfb 100644 --- a/src/commands/settings/tracks.ts +++ b/src/commands/settings/tracks.ts @@ -28,6 +28,7 @@ import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import getEmojiByName from "../../utils/getEmojiByName.js"; import ellipsis from "../../utils/ellipsis.js"; import { modalInteractionCollector } from "../../utils/dualCollector.js"; +import _ from "lodash"; const { renderRole } = client.logger; @@ -96,7 +97,8 @@ const editName = async ( }; const reorderTracks = async ( - interaction: ButtonInteraction, + interaction: ButtonInteraction | StringSelectMenuInteraction, + buttonInteraction: ButtonInteraction, m: Message, roles: Collection, currentObj: string[] @@ -132,7 +134,7 @@ const reorderTracks = async ( let out: StringSelectMenuInteraction | ButtonInteraction | null; try { out = (await m.awaitMessageComponent({ - filter: (i) => i.channel!.id === interaction.channel!.id, + filter: (i) => i.channel!.id === buttonInteraction.channel!.id, time: 300000 })) as StringSelectMenuInteraction | ButtonInteraction | null; } catch (e) { @@ -152,6 +154,14 @@ const reorderTracks = async ( return newOrder; }; +const defaultTrackData = { + name: "", + retainPrevious: false, + nullable: true, + track: [], + manageableBy: [] +}; + const editTrack = async ( interaction: ButtonInteraction | StringSelectMenuInteraction, message: Message, @@ -160,13 +170,7 @@ const editTrack = async ( ) => { const isAdmin = (interaction.member!.permissions as PermissionsBitField).has("Administrator"); if (!current) { - current = { - name: "", - retainPrevious: false, - nullable: true, - track: [], - manageableBy: [] - }; + current = _.cloneDeep(defaultTrackData); } const roleSelect = new ActionRowBuilder().addComponents( @@ -292,7 +296,7 @@ const editTrack = async ( } case "reorder": { out.deferUpdate(); - current.track = (await reorderTracks(out, message, roles, current.track))!; + current.track = (await reorderTracks(interaction, out, message, roles, current.track))!; break; } case "retainPrevious": { @@ -382,7 +386,7 @@ const callback = async (interaction: CommandInteraction) => { .setCustomId("next") .setEmoji(getEmojiByName("CONTROL.RIGHT", "id") as APIMessageComponentEmoji) .setStyle(ButtonStyle.Primary) - .setDisabled(page === tracks.length - 1), + .setDisabled(page === tracks.length - 1 || noTracks), new ButtonBuilder() .setCustomId("add") .setLabel("New Track") @@ -466,7 +470,7 @@ const callback = async (interaction: CommandInteraction) => { } case "add": { const newPage = await editTrack(i, m, roles); - if (!newPage) break; + if (_.isEqual(newPage, defaultTrackData)) break; tracks.push(); page = tracks.length - 1; break; diff --git a/src/utils/database.ts b/src/utils/database.ts index a5d5149..75a79d9 100644 --- a/src/utils/database.ts +++ b/src/utils/database.ts @@ -221,7 +221,7 @@ interface TranscriptSchema { interface findDocSchema { channelID: string; messageID: string; - transcript: string; + code: string; } export class Transcript { @@ -284,16 +284,20 @@ export class Transcript { async deleteAll(guild: string) { // console.log("Transcript delete") const filteredDocs = await this.transcripts.find({ guild: guild }).toArray(); + const filteredDocs1 = await this.messageToTranscript.find({ guild: guild }).toArray(); for (const doc of filteredDocs) { await this.transcripts.deleteOne({ code: doc.code }); } + for (const doc of filteredDocs1) { + await this.messageToTranscript.deleteOne({ code: doc.code }); + } } async readEncrypted(code: string) { // console.log("Transcript read") let doc: TranscriptSchema | null = await this.transcripts.findOne({ code: code }); let findDoc: findDocSchema | null = null; - if (!doc) findDoc = await this.messageToTranscript.findOne({ transcript: code }); + if (!doc) findDoc = await this.messageToTranscript.findOne({ code: code }); if (findDoc) { const message = await ( client.channels.cache.get(findDoc.channelID) as Discord.TextBasedChannel | null @@ -330,7 +334,7 @@ export class Transcript { let doc: TranscriptSchema | null = await this.transcripts.findOne({ code: code }); let findDoc: findDocSchema | null = null; console.log(doc); - if (!doc) findDoc = await this.messageToTranscript.findOne({ transcript: code }); + if (!doc) findDoc = await this.messageToTranscript.findOne({ code: code }); if (findDoc) { const message = await ( client.channels.cache.get(findDoc.channelID) as Discord.TextBasedChannel | null @@ -412,7 +416,7 @@ export class Transcript { topRole: { color: message.member ? message.member.roles.highest.color : 0x000000 }, - iconURL: (message.member?.user || message.author).displayAvatarURL({ forceStatic: true }), + iconURL: (message.member?.user ?? message.author).displayAvatarURL({ forceStatic: true }), bot: message.author.bot || false }, createdTimestamp: message.createdTimestamp diff --git a/src/utils/eventScheduler.ts b/src/utils/eventScheduler.ts index 2ef5fb4..34bb28e 100644 --- a/src/utils/eventScheduler.ts +++ b/src/utils/eventScheduler.ts @@ -40,7 +40,7 @@ class EventScheduler { await client.database.transcripts.upload({ channelID: channel.id, messageID: m.id, - transcript: job.attrs.data.transcript + code: job.attrs.data.transcript }); await job.remove(); });