From 4a6d57102740ae7070565afb764e08e34efa0786 Mon Sep 17 00:00:00 2001 From: TheCodedProf Date: Thu, 19 Jan 2023 15:54:40 -0500 Subject: [PATCH] intermediate update --- src/actions/roleMenu.ts | 4 +- src/commands/mod/about.ts | 10 +- src/commands/mod/viewas.ts | 6 +- src/commands/nucleus/stats.ts | 2 +- src/commands/settings/commands.ts | 2 +- src/commands/settings/logs/attachment.ts | 2 +- src/commands/settings/logs/channel.ts | 2 +- src/commands/settings/logs/events.ts | 4 +- src/commands/settings/logs/staff.ts | 2 +- src/commands/settings/stats.ts | 270 +++++++++++++++++++++-- src/commands/settings/tickets.ts | 17 +- src/commands/settings/verify.ts | 10 +- src/commands/settings/welcome.ts | 2 +- src/config/emojis.json | 8 +- src/config/format.ts | 1 - src/reflex/guide.ts | 5 +- src/reflex/verify.ts | 4 +- src/utils/confirmationMessage.ts | 2 +- src/utils/convertCurlyBracketString.ts | 2 +- src/utils/dualCollector.ts | 23 +- src/utils/log.ts | 3 +- 21 files changed, 308 insertions(+), 73 deletions(-) diff --git a/src/actions/roleMenu.ts b/src/actions/roleMenu.ts index 7056fe6..31a9fcf 100644 --- a/src/actions/roleMenu.ts +++ b/src/actions/roleMenu.ts @@ -75,7 +75,7 @@ export async function callback(interaction: CommandInteraction | ButtonInteracti const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; let valid = false; while (!valid) { - itt += 1; + itt ++; code = ""; for (let i = 0; i < length; i++) { code += chars.charAt(Math.floor(Math.random() * chars.length)); @@ -83,7 +83,7 @@ export async function callback(interaction: CommandInteraction | ButtonInteracti if (code in client.roleMenu) continue; if (itt > 1000) { itt = 0; - length += 1; + length ++; continue; } valid = true; diff --git a/src/commands/mod/about.ts b/src/commands/mod/about.ts index 130cdbc..1f53afb 100644 --- a/src/commands/mod/about.ts +++ b/src/commands/mod/about.ts @@ -12,6 +12,7 @@ import Discord, { ButtonStyle, StringSelectMenuInteraction, TextInputStyle, + APIMessageComponentEmoji, } from "discord.js"; import type { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; @@ -167,8 +168,7 @@ async function showHistory(member: Discord.GuildMember, interaction: CommandInte .setLabel(value.text) .setValue(key) .setDefault(filteredTypes.includes(key)) - // @ts-expect-error - .setEmoji(getEmojiByName(value.emoji, "id")) // FIXME: This gives a type error but is valid + .setEmoji(getEmojiByName(value.emoji, "id") as APIMessageComponentEmoji) ))) ]); components = components.concat([new ActionRowBuilder().addComponents([ @@ -270,8 +270,8 @@ async function showHistory(member: Discord.GuildMember, interaction: CommandInte continue; } i.deferUpdate(); - if (i.customId === "filter") { - filteredTypes = (i as StringSelectMenuInteraction).values; + if (i.customId === "filter" && i.isStringSelectMenu()) { + filteredTypes = i.values; pageIndex = null; refresh = true; } @@ -412,7 +412,7 @@ const callback = async (interaction: CommandInteraction): Promise => { timedOut = true; continue; } - if (out === null) { + if (out === null || out.isButton()) { continue; } else if (out instanceof ModalSubmitInteraction) { let toAdd = out.fields.getTextInputValue("note") || null; diff --git a/src/commands/mod/viewas.ts b/src/commands/mod/viewas.ts index b176dd4..4b6899c 100644 --- a/src/commands/mod/viewas.ts +++ b/src/commands/mod/viewas.ts @@ -7,7 +7,8 @@ import Discord, { ButtonStyle, NonThreadGuildBasedChannel, StringSelectMenuOptionBuilder, - StringSelectMenuBuilder + StringSelectMenuBuilder, + APIMessageComponentEmoji } from "discord.js"; import type { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import type { GuildBasedChannel } from "discord.js"; @@ -126,8 +127,7 @@ const callback = async (interaction: CommandInteraction): Promise => { return new StringSelectMenuOptionBuilder() .setLabel(c) .setValue((set * 25 + i).toString()) - // @ts-expect-error - .setEmoji(getEmojiByName("ICONS.CHANNEL.CATEGORY", "id")) // Again, this is valid but TS doesn't think so + .setEmoji(getEmojiByName("ICONS.CHANNEL.CATEGORY", "id") as APIMessageComponentEmoji) // Again, this is valid but TS doesn't think so .setDefault((set * 25 + i) === page) })) )} diff --git a/src/commands/nucleus/stats.ts b/src/commands/nucleus/stats.ts index d8b2807..8330fbe 100644 --- a/src/commands/nucleus/stats.ts +++ b/src/commands/nucleus/stats.ts @@ -13,7 +13,7 @@ const callback = async (interaction: CommandInteraction): Promise => { .setTitle("Stats") .setDescription(`**Servers:** ${client.guilds.cache.size}\n` + `**Ping:** \`${client.ws.ping * 2}ms\``) .setStatus("Success") - .setEmoji("GUILD.GRAPHS") + .setEmoji("SETTINGS.STATS.GREEN") ], ephemeral: true }); diff --git a/src/commands/settings/commands.ts b/src/commands/settings/commands.ts index 25034b2..5cafcc5 100644 --- a/src/commands/settings/commands.ts +++ b/src/commands/settings/commands.ts @@ -191,7 +191,7 @@ const callback = async (interaction: CommandInteraction): Promise => { } catch (e) { continue; } - if (!out) continue + if (!out || out.isButton()) continue const buttonText = out.fields.getTextInputValue("name"); const buttonLink = out.fields.getTextInputValue("url").replace(/{id}/gi, "{id}"); const current = chosen; diff --git a/src/commands/settings/logs/attachment.ts b/src/commands/settings/logs/attachment.ts index 6fb2461..e9e8dce 100644 --- a/src/commands/settings/logs/attachment.ts +++ b/src/commands/settings/logs/attachment.ts @@ -158,7 +158,7 @@ const callback = async (interaction: CommandInteraction): Promise => { } i.deferUpdate(); if ((i.component as unknown as ButtonInteraction).customId === "clear") { - clicks += 1; + clicks ++; if (clicks === 2) { clicks = 0; await client.database.guilds.write(interaction.guild!.id, null, ["logging.announcements.channel"]); diff --git a/src/commands/settings/logs/channel.ts b/src/commands/settings/logs/channel.ts index 9b594e8..0852e67 100644 --- a/src/commands/settings/logs/channel.ts +++ b/src/commands/settings/logs/channel.ts @@ -151,7 +151,7 @@ const callback = async (interaction: CommandInteraction): Promise => { i = i! i.deferUpdate(); if ((i.component as ButtonComponent).customId === "clear") { - clicks += 1; + clicks ++; if (clicks === 2) { clicks = 0; await client.database.guilds.write(interaction.guild!.id, null, ["logging.logs.channel"]); diff --git a/src/commands/settings/logs/events.ts b/src/commands/settings/logs/events.ts index fbe79fa..7332c7b 100644 --- a/src/commands/settings/logs/events.ts +++ b/src/commands/settings/logs/events.ts @@ -83,8 +83,8 @@ const callback = async (interaction: CommandInteraction): Promise => { continue; } i.deferUpdate(); - if (i.customId === "logs") { - const selected = (i as StringSelectMenuInteraction).values; + 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 diff --git a/src/commands/settings/logs/staff.ts b/src/commands/settings/logs/staff.ts index 3a0f395..d6379f0 100644 --- a/src/commands/settings/logs/staff.ts +++ b/src/commands/settings/logs/staff.ts @@ -155,7 +155,7 @@ const callback = async (interaction: CommandInteraction): Promise => { } i.deferUpdate(); if ((i.component as ButtonComponent).customId === "clear") { - clicks += 1; + clicks ++; if (clicks === 2) { clicks = 0; await client.database.guilds.write(interaction.guild.id, null, ["logging.staff.channel"]); diff --git a/src/commands/settings/stats.ts b/src/commands/settings/stats.ts index 1093bd2..df99510 100644 --- a/src/commands/settings/stats.ts +++ b/src/commands/settings/stats.ts @@ -1,45 +1,283 @@ import { LoadingEmbed } from "../../utils/defaults.js"; -import Discord, { CommandInteraction, Message, ActionRowBuilder, GuildMember, StringSelectMenuBuilder, StringSelectMenuInteraction, SelectMenuOptionBuilder } from "discord.js"; +import Discord, { CommandInteraction, Message, ActionRowBuilder, StringSelectMenuBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuOptionBuilder, APIMessageComponentEmoji, MessageComponentInteraction, TextInputBuilder } from "discord.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; -import confirmationMessage from "../../utils/confirmationMessage.js"; import type { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import client from "../../utils/client.js"; import convertCurlyBracketString from "../../utils/convertCurlyBracketString.js"; import { callback as statsChannelAddCallback } from "../../reflex/statsChannelUpdate.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("stats") .setDescription("Controls channels which update when someone joins or leaves the server") +type ChangesType = Record + +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; // stats: Record - if (!stats) { - await interaction.editReply({embeds: [new EmojiEmbed() - .setTitle("Stats channels") - .setDescription("You don't have ant stats channels yet") - .setStatus("Success") - .setEmoji("") - ]}) - } + const stats = config.stats; + let currentID = ""; + let current: { + name: string; + enabled: boolean; + } = { + name: "", + enabled: false + }; + let description = ""; let pageSelect = new StringSelectMenuBuilder() .setCustomId("page") .setPlaceholder("Select a stats channel to manage") + .setDisabled(Object.keys(stats).length === 0) .setMinValues(1) .setMaxValues(1); - for (const [id, { name, enabled }] of Object.entries(stats)) { - pageSelect.addOption() + let 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]!; + // Propogate pageSelect with list of stats channels + 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() + .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), + ); + + let 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().addComponents(pageSelect), + new ActionRowBuilder().addComponents(actionSelect), + row + ] + }); + let i: MessageComponentInteraction; + try { + i = await m.awaitMessageComponent({ filter: (i) => i.user.id === interaction.user.id, time: 30000 }); + } catch (e) { + closed = true; + continue; + } + if (i.isStringSelectMenu()) { + switch(i.customId) { + case "page": + page = Object.keys(stats).indexOf(i.values[0]!); + 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().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().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().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().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().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] = {}; + i.deferUpdate(); + break; + case "toggleEnabled": + changes[currentID]!.enabled = !stats[currentID]!.enabled; + i.deferUpdate(); + break; + } + break; + } + } else if (i.isButton()) { + i.deferUpdate(); + switch(i.customId) { + case "back": + page--; + break; + case "next": + page++; + break; + case "add": + break; + case "save": + let changed = applyChanges(config.stats, changes); + singleNotify("statsChannelDeleted", interaction.guild.id, true) + config.stats = changed; + changes = {} + await client.database.guilds.write(interaction.guildId!, config); + } } - // [ Action... ] -> Edit, delete, reorder - // [Back][Next][Add] + console.log(changes, config.stats); } while (!closed); - closed = true; + } catch(e) { + console.log(e) + } }; const check = (interaction: CommandInteraction) => { diff --git a/src/commands/settings/tickets.ts b/src/commands/settings/tickets.ts index aa25b69..8f9f688 100644 --- a/src/commands/settings/tickets.ts +++ b/src/commands/settings/tickets.ts @@ -11,11 +11,9 @@ import Discord, { MessageComponentInteraction, StringSelectMenuBuilder, Role, - StringSelectMenuInteraction, ButtonStyle, TextInputBuilder, ButtonComponent, - StringSelectMenuComponent, ModalSubmitInteraction, APIMessageComponentEmoji } from "discord.js"; @@ -390,14 +388,14 @@ const callback = async (interaction: CommandInteraction): Promise => { innerTimedOut = true; continue; } - if ((i.component as StringSelectMenuComponent).customId === "template") { + if (i.isStringSelectMenu() && i.customId === "template") { i.deferUpdate(); await interaction.channel!.send({ embeds: [ new EmojiEmbed() - .setTitle(ticketMessages[parseInt((i as StringSelectMenuInteraction).values[0]!)]!.label) + .setTitle(ticketMessages[parseInt(i.values[0]!)]!.label) .setDescription( - ticketMessages[parseInt((i as StringSelectMenuInteraction).values[0]!)]!.description + ticketMessages[parseInt(i.values[0]!)]!.description ) .setStatus("Success") .setEmoji("GUILD.TICKET.OPEN") @@ -643,16 +641,16 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t timedOut = true; continue; } - if ((i.component as StringSelectMenuComponent).customId === "types") { + if (i.isStringSelectMenu() && i.customId === "types") { i.deferUpdate(); - const types = toHexInteger((i as StringSelectMenuInteraction).values, ticketTypes); + const types = toHexInteger(i.values, ticketTypes); await client.database.guilds.write(interaction.guild!.id, { "tickets.types": types }); data.types = types; - } else if ((i.component as StringSelectMenuComponent).customId === "removeTypes") { + } else if (i.isStringSelectMenu() && i.customId === "removeTypes") { i.deferUpdate(); - const types = (i as StringSelectMenuInteraction).values; + const types = i.values; let customTypes = data.customTypes; if (customTypes) { customTypes = customTypes.filter((t) => !types.includes(t)); @@ -708,6 +706,7 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t } catch (e) { continue; } + if (!out || out.isButton()) continue; out = out as ModalSubmitInteraction; let toAdd = out.fields.getTextInputValue("type"); if (!toAdd) { diff --git a/src/commands/settings/verify.ts b/src/commands/settings/verify.ts index 0f9f4a0..d3971a8 100644 --- a/src/commands/settings/verify.ts +++ b/src/commands/settings/verify.ts @@ -10,10 +10,8 @@ import Discord, { Role, ButtonStyle, StringSelectMenuBuilder, - StringSelectMenuComponent, TextInputBuilder, EmbedBuilder, - StringSelectMenuInteraction, ButtonComponent } from "discord.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; @@ -170,7 +168,7 @@ const callback = async (interaction: CommandInteraction): Promise => { } i.deferUpdate(); if ((i.component as ButtonComponent).customId === "clear") { - clicks += 1; + clicks ++; if (clicks === 2) { clicks = 0; await client.database.guilds.write(interaction.guild.id, null, ["verify.role", "verify.enabled"]); @@ -257,14 +255,14 @@ const callback = async (interaction: CommandInteraction): Promise => { innerTimedOut = true; continue; } - if ((i.component as StringSelectMenuComponent).customId === "template") { + if (i.isStringSelectMenu() && i.customId === "template") { i.deferUpdate(); await interaction.channel!.send({ embeds: [ new EmojiEmbed() - .setTitle(verifyMessages[parseInt((i as StringSelectMenuInteraction).values[0]!)]!.label) + .setTitle(verifyMessages[parseInt(i.values[0]!)]!.label) .setDescription( - verifyMessages[parseInt((i as StringSelectMenuInteraction).values[0]!)]!.description + verifyMessages[parseInt(i.values[0]!)]!.description ) .setStatus("Success") .setEmoji("CONTROL.BLOCKTICK") diff --git a/src/commands/settings/welcome.ts b/src/commands/settings/welcome.ts index 42376e8..abedec5 100644 --- a/src/commands/settings/welcome.ts +++ b/src/commands/settings/welcome.ts @@ -309,7 +309,7 @@ const check = (interaction: CommandInteraction) => { }; const autocomplete = async (interaction: AutocompleteInteraction): Promise => { - const validReplacements = ["serverName", "memberCount", "memberCount:bots", "memberCount:humans"] + const validReplacements = ["serverName", "memberCount:all", "memberCount:bots", "memberCount:humans"] if (!interaction.guild) return []; const prompt = interaction.options.getString("message"); const autocompletions = []; diff --git a/src/config/emojis.json b/src/config/emojis.json index 26d4c98..8c45353 100644 --- a/src/config/emojis.json +++ b/src/config/emojis.json @@ -22,6 +22,7 @@ "FILTER": "990242059451514902", "ATTACHMENT": "997570687193587812", "LOGGING": "999613304446144562", + "SAVE": "1065722246322200586", "NOTIFY": { "ON": "1000726394579464232", "OFF": "1000726363495477368" @@ -210,6 +211,12 @@ "STOP": "853519660116738078" } }, + "SETTINGS": { + "STATS": { + "GREEN": "752214059159650396", + "RED": "1065677252630675556" + } + }, "GUILD": { "RED": "959779988264079361", "YELLOW": "729763053352124529", @@ -219,7 +226,6 @@ "EDIT": "729066518549233795", "DELETE": "953035210121953320" }, - "GRAPHS": "752214059159650396", "SETTINGS": "752570111063228507", "ICONCHANGE": "729763053612302356", "TICKET": { diff --git a/src/config/format.ts b/src/config/format.ts index 929552f..b00b3e4 100644 --- a/src/config/format.ts +++ b/src/config/format.ts @@ -1,5 +1,4 @@ import fs from "fs"; -// @ts-expect-error import * as readLine from "node:readline/promises"; const defaultDict: Record = { diff --git a/src/reflex/guide.ts b/src/reflex/guide.ts index 6829ef2..3971ad9 100644 --- a/src/reflex/guide.ts +++ b/src/reflex/guide.ts @@ -3,7 +3,6 @@ import Discord, { ActionRowBuilder, ButtonBuilder, MessageComponentInteraction, - StringSelectMenuInteraction, Guild, CommandInteraction, GuildTextBasedChannel, @@ -285,8 +284,8 @@ export default async (guild: Guild, interaction?: CommandInteraction) => { selectPaneOpen = false; } else if (i.component.customId === "select") { selectPaneOpen = !selectPaneOpen; - } else if (i.component.customId === "page") { - page = parseInt((i as StringSelectMenuInteraction).values[0]!); + } else if (i.isStringSelectMenu() && i.component.customId === "page") { + page = parseInt(i.values[0]!); selectPaneOpen = false; } else { cancelled = true; diff --git a/src/reflex/verify.ts b/src/reflex/verify.ts index 573da5e..f7d0396 100644 --- a/src/reflex/verify.ts +++ b/src/reflex/verify.ts @@ -182,14 +182,14 @@ export default async function (interaction: CommandInteraction | ButtonInteracti let itt = 0; const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; do { - itt += 1; + itt ++; code = ""; for (let i = 0; i < length; i++) { code += chars.charAt(Math.floor(Math.random() * chars.length)); } if (itt > 1000) { itt = 0; - length += 1; + length ++; } } while (code in verify); const role: Role | null = await interaction.guild!.roles.fetch(config.verify.role); diff --git a/src/utils/confirmationMessage.ts b/src/utils/confirmationMessage.ts index 4d90676..613b48c 100644 --- a/src/utils/confirmationMessage.ts +++ b/src/utils/confirmationMessage.ts @@ -257,7 +257,7 @@ class confirmationMessage { cancelled = true; continue; } - if (out === null) { + if (out === null || out.isButton()) { cancelled = true; continue; } diff --git a/src/utils/convertCurlyBracketString.ts b/src/utils/convertCurlyBracketString.ts index 5d2c23d..058ba16 100644 --- a/src/utils/convertCurlyBracketString.ts +++ b/src/utils/convertCurlyBracketString.ts @@ -13,7 +13,7 @@ async function convertCurlyBracketString( .replace("{member:mention}", memberID ? `<@${memberID}>` : "{member:mention}") .replace("{member:name}", memberName ? `${memberName}` : "{member:name}") .replace("{serverName}", serverName ? `${serverName}` : "{serverName}") - .replace("{memberCount}", memberCount ? `${memberCount}` : "{memberCount}") + .replace("{memberCount:all}", memberCount ? `${memberCount}` : "{memberCount}") .replace("{memberCount:bots}", bots ? `${bots}` : "{memberCount:bots}") .replace("{memberCount:humans}", memberCount && bots ? `${memberCount - bots}` : "{memberCount:humans}"); diff --git a/src/utils/dualCollector.ts b/src/utils/dualCollector.ts index 714a2d9..3c2b5d9 100644 --- a/src/utils/dualCollector.ts +++ b/src/utils/dualCollector.ts @@ -1,4 +1,4 @@ -import Discord, { Client, Interaction, Message, MessageComponentInteraction } from "discord.js"; +import Discord, { ButtonInteraction, Client, Interaction, InteractionCollector, Message, MessageComponentInteraction, ModalSubmitInteraction } from "discord.js"; import client from "./client.js"; export default async function ( @@ -49,8 +49,8 @@ export async function modalInteractionCollector( m: Message, modalFilter: (i: Interaction) => boolean | Promise, interactionFilter: (i: MessageComponentInteraction) => boolean | Promise -): Promise { - let out: Interaction; +): Promise { + let out: ButtonInteraction | ModalSubmitInteraction; try { out = await new Promise((resolve, _reject) => { const int = m @@ -58,22 +58,17 @@ export async function modalInteractionCollector( filter: (i: MessageComponentInteraction) => interactionFilter(i), time: 300000 }) - .on("collect", (i: Interaction) => { + .on("collect", (i: ButtonInteraction) => { + mod.stop(); resolve(i); }); - const mod = new Discord.InteractionCollector(client as Client, { + const mod = new InteractionCollector(client as Client, { filter: (i: Interaction) => modalFilter(i), time: 300000 - }).on("collect", async (i: Interaction) => { - int.stop(); - (i as Discord.ModalSubmitInteraction).deferUpdate(); - resolve(i as Discord.ModalSubmitInteraction); - }); - int.on("end", () => { - mod.stop(); - }); - mod.on("end", () => { + }).on("collect", async (i: ModalSubmitInteraction) => { int.stop(); + await i.deferUpdate(); + resolve(i); }); }); } catch (e) { diff --git a/src/utils/log.ts b/src/utils/log.ts index 54f656a..184f29a 100644 --- a/src/utils/log.ts +++ b/src/utils/log.ts @@ -29,7 +29,8 @@ export const Logger = { if (typeof value === "number") value = value.toString(); return { value: value, displayValue: displayValue }; }, - renderChannel(channel: Discord.GuildChannel | Discord.ThreadChannel) { + renderChannel(channel: Discord.GuildChannel | Discord.ThreadChannel | string) { + if (typeof channel === "string") channel = client.channels.cache.get(channel) as Discord.GuildChannel | Discord.ThreadChannel; return `${channel.name} [<#${channel.id}>]`; }, renderRole(role: Discord.Role) {