diff --git a/.gitignore b/.gitignore index bfedc85..db17304 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ src/config/* !src/config/emojis.json src/config/main.json .vscode/ +.vim/ yarn-error.log yarn.lock src/utils/temp/*.png @@ -15,4 +16,4 @@ src/utils/temp/*.jpeg src/utils/temp/*.jpg ClicksMigratingProblems/oldData/ -ClicksMigratingProblems/oldData copy/ \ No newline at end of file +ClicksMigratingProblems/oldData copy/ diff --git a/src/commands/mod/ban.ts b/src/commands/mod/ban.ts index 4f2d4d7..9a82970 100644 --- a/src/commands/mod/ban.ts +++ b/src/commands/mod/ban.ts @@ -26,7 +26,9 @@ const callback = async (interaction: CommandInteraction): Promise => { let reason = null; let notify = true; let confirmation; - while (true) { + let cancelled = false; + let timedOut = false; + while (!timedOut && !cancelled) { confirmation = await new confirmationMessage(interaction) .setEmoji("PUNISH.BAN.RED") .setTitle("Ban") @@ -46,114 +48,101 @@ const callback = async (interaction: CommandInteraction): Promise => { .addReasonButton(reason ?? "") .send(reason !== null); reason = reason ?? ""; - if (confirmation.cancelled) return; - if (confirmation.success) break; - if (confirmation.newReason) reason = confirmation.newReason; - if (confirmation.components) notify = confirmation.components.notify.active; + if (confirmation.cancelled) timedOut = true; + else if (confirmation.success) cancelled = true; + else if (confirmation.newReason) reason = confirmation.newReason; + else if (confirmation.components) notify = confirmation.components.notify.active; } - if (confirmation.success) { - let dmd = false; - let dm; - const config = await client.database.guilds.read(interaction.guild.id); - try { - if (notify) { - dm = await (interaction.options.getMember("user") as GuildMember).send({ - embeds: [ - new EmojiEmbed() - .setEmoji("PUNISH.BAN.RED") - .setTitle("Banned") - .setDescription( - `You have been banned in ${interaction.guild.name}` + - (reason ? ` for:\n> ${reason}` : ".") - ) - .setStatus("Danger") - ], - components: [ - new MessageActionRow().addComponents( - config.moderation.ban.text - ? [ - new MessageButton() - .setStyle("LINK") - .setLabel(config.moderation.ban.text) - .setURL(config.moderation.ban.link) - ] - : [] - ) - ] - }); - dmd = true; - } - } catch { - dmd = false; - } - try { - const member = interaction.options.getMember("user") as GuildMember; - member.ban({ - days: Number(interaction.options.getNumber("delete") ?? 0), - reason: reason ?? "No reason provided" - }); - await client.database.history.create("ban", interaction.guild.id, member.user, interaction.user, reason); - const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger; - const data = { - meta: { - type: "memberBan", - displayName: "Member Banned", - calculateType: "guildMemberPunish", - color: NucleusColors.red, - emoji: "PUNISH.BAN.RED", - timestamp: new Date().getTime() - }, - list: { - memberId: entry(member.user.id, `\`${member.user.id}\``), - name: entry(member.user.id, renderUser(member.user)), - banned: entry(new Date().getTime(), renderDelta(new Date().getTime())), - bannedBy: entry(interaction.user.id, renderUser(interaction.user)), - reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"), - accountCreated: entry(member.user.createdAt, renderDelta(member.user.createdAt)), - serverMemberCount: interaction.guild.memberCount - }, - hidden: { - guild: interaction.guild.id - } - }; - log(data); - } catch { - await interaction.editReply({ + if (timedOut) return; + let dmd = false; + let dm; + const config = await client.database.guilds.read(interaction.guild.id); + try { + if (notify) { + dm = await (interaction.options.getMember("user") as GuildMember).send({ embeds: [ new EmojiEmbed() .setEmoji("PUNISH.BAN.RED") - .setTitle("Ban") - .setDescription("Something went wrong and the user was not banned") + .setTitle("Banned") + .setDescription( + `You have been banned in ${interaction.guild.name}` + (reason ? ` for:\n> ${reason}` : ".") + ) .setStatus("Danger") ], - components: [] + components: [ + new MessageActionRow().addComponents( + config.moderation.ban.text + ? [ + new MessageButton() + .setStyle("LINK") + .setLabel(config.moderation.ban.text) + .setURL(config.moderation.ban.link) + ] + : [] + ) + ] }); - if (dmd) await dm.delete(); - return; + dmd = true; } - const failed = !dmd && notify; - await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setEmoji(`PUNISH.BAN.${failed ? "YELLOW" : "GREEN"}`) - .setTitle("Ban") - .setDescription("The member was banned" + (failed ? ", but could not be notified" : "")) - .setStatus(failed ? "Warning" : "Success") - ], - components: [] + } catch { + dmd = false; + } + try { + const member = interaction.options.getMember("user") as GuildMember; + member.ban({ + days: Number(interaction.options.getNumber("delete") ?? 0), + reason: reason ?? "No reason provided" }); - } else { + await client.database.history.create("ban", interaction.guild.id, member.user, interaction.user, reason); + const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger; + const data = { + meta: { + type: "memberBan", + displayName: "Member Banned", + calculateType: "guildMemberPunish", + color: NucleusColors.red, + emoji: "PUNISH.BAN.RED", + timestamp: new Date().getTime() + }, + list: { + memberId: entry(member.user.id, `\`${member.user.id}\``), + name: entry(member.user.id, renderUser(member.user)), + banned: entry(new Date().getTime(), renderDelta(new Date().getTime())), + bannedBy: entry(interaction.user.id, renderUser(interaction.user)), + reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"), + accountCreated: entry(member.user.createdAt, renderDelta(member.user.createdAt)), + serverMemberCount: interaction.guild.memberCount + }, + hidden: { + guild: interaction.guild.id + } + }; + log(data); + } catch { await interaction.editReply({ embeds: [ new EmojiEmbed() - .setEmoji("PUNISH.BAN.GREEN") + .setEmoji("PUNISH.BAN.RED") .setTitle("Ban") - .setDescription("No changes were made") - .setStatus("Success") + .setDescription("Something went wrong and the user was not banned") + .setStatus("Danger") ], components: [] }); + if (dmd) await dm.delete(); + return; } + const failed = !dmd && notify; + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setEmoji(`PUNISH.BAN.${failed ? "YELLOW" : "GREEN"}`) + .setTitle("Ban") + .setDescription("The member was banned" + (failed ? ", but could not be notified" : "")) + .setStatus(failed ? "Warning" : "Success") + ], + components: [] + }); }; const check = (interaction: CommandInteraction) => { diff --git a/src/commands/mod/info.ts b/src/commands/mod/info.ts index f0257f8..da2d540 100644 --- a/src/commands/mod/info.ts +++ b/src/commands/mod/info.ts @@ -103,7 +103,9 @@ async function showHistory(member: Discord.GuildMember, interaction: CommandInte let refresh = true; let filteredTypes: string[] = []; let openFilterPane = false; - while (true) { + let timedOut = false; + let showHistorySelected = false; + while (!timedOut && !showHistorySelected) { if (refresh) { history = await client.database.history.read(member.guild.id, member.id, currentYear); history = history @@ -249,7 +251,8 @@ async function showHistory(member: Discord.GuildMember, interaction: CommandInte .setFooter({ text: "Message timed out" }) ] }); - return 0; + timedOut = true; + continue; } i.deferUpdate(); if (i.customId === "filter") { @@ -289,13 +292,14 @@ async function showHistory(member: Discord.GuildMember, interaction: CommandInte refresh = true; } if (i.customId === "modNotes") { - return 1; + showHistorySelected = true; } if (i.customId === "openFilter") { openFilterPane = !openFilterPane; refresh = true; } } + return timedOut ? 0 : 1; } const callback = async (interaction: CommandInteraction): Promise => { @@ -308,7 +312,8 @@ const callback = async (interaction: CommandInteraction): Promise => { }); let note; let firstLoad = true; - while (true) { + let timedOut = false; + while (!timedOut) { note = await client.database.notes.read(member.guild.id, member.id); if (firstLoad && !note) { await showHistory(member, interaction); @@ -341,7 +346,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - return; + timedOut = true; + continue; } if (i.customId === "modify") { await i.showModal( @@ -387,7 +393,8 @@ const callback = async (interaction: CommandInteraction): Promise => { (m) => m.customId === "modify" ); } catch (e) { - break; + timedOut = true; + continue; } if (out === null) { continue; diff --git a/src/commands/mod/kick.ts b/src/commands/mod/kick.ts index d5b5b7a..ef58067 100644 --- a/src/commands/mod/kick.ts +++ b/src/commands/mod/kick.ts @@ -18,7 +18,9 @@ const callback = async (interaction: CommandInteraction): Promise => { let reason = null; let notify = true; let confirmation; - while (true) { + let timedOut = false; + let success = false; + while (!timedOut && !success) { confirmation = await new confirmationMessage(interaction) .setEmoji("PUNISH.KICK.RED") .setTitle("Kick") @@ -34,120 +36,107 @@ const callback = async (interaction: CommandInteraction): Promise => { .addReasonButton(reason ?? "") .send(reason !== null); reason = reason ?? ""; - if (confirmation.cancelled) return; - if (confirmation.success) break; - if (confirmation.newReason) reason = confirmation.newReason; - if (confirmation.components) { + if (confirmation.cancelled) timedOut = true; + else if (confirmation.success) success = true; + else if (confirmation.newReason) reason = confirmation.newReason; + else if (confirmation.components) { notify = confirmation.components.notify.active; } } - if (confirmation.success) { - let dmd = false; - let dm; - const config = await client.database.guilds.read(interaction.guild.id); - try { - if (notify) { - dm = await (interaction.options.getMember("user") as GuildMember).send({ - embeds: [ - new EmojiEmbed() - .setEmoji("PUNISH.KICK.RED") - .setTitle("Kicked") - .setDescription( - `You have been kicked in ${interaction.guild.name}` + - (reason ? ` for:\n> ${reason}` : ".") - ) - .setStatus("Danger") - ], - components: [ - new MessageActionRow().addComponents( - config.moderation.kick.text - ? [ - new MessageButton() - .setStyle("LINK") - .setLabel(config.moderation.kick.text) - .setURL(config.moderation.kick.link) - ] - : [] - ) - ] - }); - dmd = true; - } - } catch { - dmd = false; - } - try { - (interaction.options.getMember("user") as GuildMember).kick(reason ?? "No reason provided."); - const member = interaction.options.getMember("user") as GuildMember; - await client.database.history.create("kick", interaction.guild.id, member.user, interaction.user, reason); - const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger; - const data = { - meta: { - type: "memberKick", - displayName: "Member Kicked", - calculateType: "guildMemberPunish", - color: NucleusColors.red, - emoji: "PUNISH.KICK.RED", - timestamp: new Date().getTime() - }, - list: { - memberId: entry(member.id, `\`${member.id}\``), - name: entry(member.id, renderUser(member.user)), - joined: entry(member.joinedAt, renderDelta(member.joinedAt)), - kicked: entry(new Date().getTime(), renderDelta(new Date().getTime())), - kickedBy: entry(interaction.user.id, renderUser(interaction.user)), - reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"), - timeInServer: entry( - new Date().getTime() - member.joinedTimestamp, - humanizeDuration(new Date().getTime() - member.joinedTimestamp, { - round: true - }) - ), - accountCreated: entry(member.user.createdAt, renderDelta(member.user.createdAt)), - serverMemberCount: member.guild.memberCount - }, - hidden: { - guild: member.guild.id - } - }; - log(data); - } catch { - await interaction.editReply({ + if (timedOut) return; + let dmd = false; + let dm; + const config = await client.database.guilds.read(interaction.guild.id); + try { + if (notify) { + dm = await (interaction.options.getMember("user") as GuildMember).send({ embeds: [ new EmojiEmbed() .setEmoji("PUNISH.KICK.RED") - .setTitle("Kick") - .setDescription("Something went wrong and the user was not kicked") + .setTitle("Kicked") + .setDescription( + `You have been kicked in ${interaction.guild.name}` + (reason ? ` for:\n> ${reason}` : ".") + ) .setStatus("Danger") ], - components: [] + components: [ + new MessageActionRow().addComponents( + config.moderation.kick.text + ? [ + new MessageButton() + .setStyle("LINK") + .setLabel(config.moderation.kick.text) + .setURL(config.moderation.kick.link) + ] + : [] + ) + ] }); - if (dmd) await dm.delete(); - return; + dmd = true; } - const failed = !dmd && notify; - await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setEmoji(`PUNISH.KICK.${failed ? "YELLOW" : "GREEN"}`) - .setTitle("Kick") - .setDescription("The member was kicked" + (failed ? ", but could not be notified" : "")) - .setStatus(failed ? "Warning" : "Success") - ], - components: [] - }); - } else { + } catch { + dmd = false; + } + try { + (interaction.options.getMember("user") as GuildMember).kick(reason ?? "No reason provided."); + const member = interaction.options.getMember("user") as GuildMember; + await client.database.history.create("kick", interaction.guild.id, member.user, interaction.user, reason); + const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger; + const data = { + meta: { + type: "memberKick", + displayName: "Member Kicked", + calculateType: "guildMemberPunish", + color: NucleusColors.red, + emoji: "PUNISH.KICK.RED", + timestamp: new Date().getTime() + }, + list: { + memberId: entry(member.id, `\`${member.id}\``), + name: entry(member.id, renderUser(member.user)), + joined: entry(member.joinedAt, renderDelta(member.joinedAt)), + kicked: entry(new Date().getTime(), renderDelta(new Date().getTime())), + kickedBy: entry(interaction.user.id, renderUser(interaction.user)), + reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"), + timeInServer: entry( + new Date().getTime() - member.joinedTimestamp, + humanizeDuration(new Date().getTime() - member.joinedTimestamp, { + round: true + }) + ), + accountCreated: entry(member.user.createdAt, renderDelta(member.user.createdAt)), + serverMemberCount: member.guild.memberCount + }, + hidden: { + guild: member.guild.id + } + }; + log(data); + } catch { await interaction.editReply({ embeds: [ new EmojiEmbed() - .setEmoji("PUNISH.KICK.GREEN") + .setEmoji("PUNISH.KICK.RED") .setTitle("Kick") - .setDescription("No changes were made") - .setStatus("Success") + .setDescription("Something went wrong and the user was not kicked") + .setStatus("Danger") ], components: [] }); + if (dmd) await dm.delete(); + return; } + const failed = !dmd && notify; + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setEmoji(`PUNISH.KICK.${failed ? "YELLOW" : "GREEN"}`) + .setTitle("Kick") + .setDescription("The member was kicked" + (failed ? ", but could not be notified" : "")) + .setStatus(failed ? "Warning" : "Success") + ], + components: [] + }); }; const check = (interaction: CommandInteraction) => { diff --git a/src/commands/mod/mute.ts b/src/commands/mod/mute.ts index 151ff76..327ecdf 100644 --- a/src/commands/mod/mute.ts +++ b/src/commands/mod/mute.ts @@ -162,7 +162,9 @@ const callback = async (interaction: CommandInteraction): Promise => { let notify = true; let createAppealTicket = false; let confirmation; - while (true) { + let timedOut = false; + let success = false; + while (!timedOut && !success) { confirmation = await new confirmationMessage(interaction) .setEmoji("PUNISH.MUTE.RED") .setTitle("Mute") @@ -203,162 +205,146 @@ const callback = async (interaction: CommandInteraction): Promise => { .addReasonButton(reason ?? "") .send(true); reason = reason ?? ""; - if (confirmation.cancelled) return; - if (confirmation.success) break; + if (confirmation.cancelled) timedOut = true; + if (confirmation.success) success = true; if (confirmation.newReason) reason = confirmation.newReason; if (confirmation.components) { notify = confirmation.components.notify.active; createAppealTicket = confirmation.components.appeal.active; } } - if (confirmation.success) { - let dmd = false; - let dm; - const config = await client.database.guilds.read(interaction.guild.id); - try { - if (notify) { - dm = await user.send({ - embeds: [ - new EmojiEmbed() - .setEmoji("PUNISH.MUTE.RED") - .setTitle("Muted") - .setDescription( - `You have been muted in ${interaction.guild.name}` + - (reason - ? ` for:\n> ${reason}` - : ".\n\n" + - `You will be unmuted at: at ()`) + - (confirmation.components.appeal.response - ? `You can appeal this here: <#${confirmation.components.appeal.response}>` - : "") - ) - .setStatus("Danger") - ], - components: [ - new MessageActionRow().addComponents( - config.moderation.mute.text - ? [ - new MessageButton() - .setStyle("LINK") - .setLabel(config.moderation.mute.text) - .setURL(config.moderation.mute.link) - ] - : [] + if (timedOut) return; + let dmd = false; + let dm; + const config = await client.database.guilds.read(interaction.guild.id); + try { + if (notify) { + dm = await user.send({ + embeds: [ + new EmojiEmbed() + .setEmoji("PUNISH.MUTE.RED") + .setTitle("Muted") + .setDescription( + `You have been muted in ${interaction.guild.name}` + + (reason + ? ` for:\n> ${reason}` + : ".\n\n" + + `You will be unmuted at: at ()`) + + (confirmation.components.appeal.response + ? `You can appeal this here: <#${confirmation.components.appeal.response}>` + : "") ) - ] - }); - dmd = true; - } - } catch { - dmd = false; - } - const member = user; - let errors = 0; - try { - if (config.moderation.mute.timeout) { - await member.timeout(muteTime * 1000, reason || "No reason provided"); - if (config.moderation.mute.role !== null) { - await member.roles.add(config.moderation.mute.role); - await client.database.eventScheduler.schedule( - "naturalUnmute", - new Date().getTime() + muteTime * 1000, - { - guild: interaction.guild.id, - user: user.id, - expires: new Date().getTime() + muteTime * 1000 - } - ); - } - } - } catch { - errors++; + .setStatus("Danger") + ], + components: [ + new MessageActionRow().addComponents( + config.moderation.mute.text + ? [ + new MessageButton() + .setStyle("LINK") + .setLabel(config.moderation.mute.text) + .setURL(config.moderation.mute.link) + ] + : [] + ) + ] + }); + dmd = true; } - try { + } catch { + dmd = false; + } + const member = user; + let errors = 0; + try { + if (config.moderation.mute.timeout) { + await member.timeout(muteTime * 1000, reason || "No reason provided"); if (config.moderation.mute.role !== null) { await member.roles.add(config.moderation.mute.role); - await client.database.eventScheduler.schedule("unmuteRole", new Date().getTime() + muteTime * 1000, { + await client.database.eventScheduler.schedule("naturalUnmute", new Date().getTime() + muteTime * 1000, { guild: interaction.guild.id, user: user.id, - role: config.moderation.mute.role + expires: new Date().getTime() + muteTime * 1000 }); } - } catch (e) { - console.log(e); - errors++; } - if (errors === 2) { - await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setEmoji("PUNISH.MUTE.RED") - .setTitle("Mute") - .setDescription("Something went wrong and the user was not muted") - .setStatus("Danger") - ], - components: [] - }); // TODO: make this clearer - if (dmd) await dm.delete(); - return; + } catch { + errors++; + } + try { + if (config.moderation.mute.role !== null) { + await member.roles.add(config.moderation.mute.role); + await client.database.eventScheduler.schedule("unmuteRole", new Date().getTime() + muteTime * 1000, { + guild: interaction.guild.id, + user: user.id, + role: config.moderation.mute.role + }); } - await client.database.history.create("mute", interaction.guild.id, member.user, interaction.user, reason); - const failed = !dmd && notify; - await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setEmoji(`PUNISH.MUTE.${failed ? "YELLOW" : "GREEN"}`) - .setTitle("Mute") - .setDescription( - "The member was muted" + - (failed ? ", but could not be notified" : "") + - (confirmation.components.appeal.response - ? ` and an appeal ticket was opened in <#${confirmation.components.appeal.response}>` - : "") - ) - .setStatus(failed ? "Warning" : "Success") - ], - components: [] - }); - const data = { - meta: { - type: "memberMute", - displayName: "Member Muted", - calculateType: "guildMemberPunish", - color: NucleusColors.yellow, - emoji: "PUNISH.WARN.YELLOW", - timestamp: new Date().getTime() - }, - list: { - memberId: entry(member.user.id, `\`${member.user.id}\``), - name: entry(member.user.id, renderUser(member.user)), - mutedUntil: entry( - new Date().getTime() + muteTime * 1000, - renderDelta(new Date().getTime() + muteTime * 1000) - ), - muted: entry(new Date().getTime(), renderDelta(new Date().getTime() - 1000)), - mutedBy: entry(interaction.member.user.id, renderUser(interaction.member.user)), - reason: entry(reason, reason ? reason : "*No reason provided*") - }, - hidden: { - guild: interaction.guild.id - } - }; - log(data); - } else { + } catch (e) { + console.log(e); + errors++; + } + if (errors === 2) { await interaction.editReply({ embeds: [ new EmojiEmbed() - .setEmoji("PUNISH.MUTE.GREEN") + .setEmoji("PUNISH.MUTE.RED") .setTitle("Mute") - .setDescription("No changes were made") - .setStatus("Success") + .setDescription("Something went wrong and the user was not muted") + .setStatus("Danger") ], components: [] - }); + }); // TODO: make this clearer + if (dmd) await dm.delete(); + return; } + await client.database.history.create("mute", interaction.guild.id, member.user, interaction.user, reason); + const failed = !dmd && notify; + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setEmoji(`PUNISH.MUTE.${failed ? "YELLOW" : "GREEN"}`) + .setTitle("Mute") + .setDescription( + "The member was muted" + + (failed ? ", but could not be notified" : "") + + (confirmation.components.appeal.response + ? ` and an appeal ticket was opened in <#${confirmation.components.appeal.response}>` + : "") + ) + .setStatus(failed ? "Warning" : "Success") + ], + components: [] + }); + const data = { + meta: { + type: "memberMute", + displayName: "Member Muted", + calculateType: "guildMemberPunish", + color: NucleusColors.yellow, + emoji: "PUNISH.WARN.YELLOW", + timestamp: new Date().getTime() + }, + list: { + memberId: entry(member.user.id, `\`${member.user.id}\``), + name: entry(member.user.id, renderUser(member.user)), + mutedUntil: entry( + new Date().getTime() + muteTime * 1000, + renderDelta(new Date().getTime() + muteTime * 1000) + ), + muted: entry(new Date().getTime(), renderDelta(new Date().getTime() - 1000)), + mutedBy: entry(interaction.member.user.id, renderUser(interaction.member.user)), + reason: entry(reason, reason ? reason : "*No reason provided*") + }, + hidden: { + guild: interaction.guild.id + } + }; + log(data); }; const check = (interaction: CommandInteraction) => { diff --git a/src/commands/mod/nick.ts b/src/commands/mod/nick.ts index c6f0633..ff1db23 100644 --- a/src/commands/mod/nick.ts +++ b/src/commands/mod/nick.ts @@ -19,7 +19,9 @@ const callback = async (interaction: CommandInteraction): Promise => { // TODO:[Modals] Replace this with a modal let notify = true; let confirmation; - while (true) { + let timedOut = false; + let success = false; + while (!timedOut && !success) { confirmation = await new confirmationMessage(interaction) .setEmoji("PUNISH.NICKNAME.RED") .setTitle("Nickname") @@ -46,121 +48,120 @@ const callback = async (interaction: CommandInteraction): Promise => { notify ) .send(interaction.options.getString("name") !== null); - if (confirmation.cancelled) return; - if (confirmation.success) break; - if (confirmation.components) { + if (confirmation.cancelled) timedOut = true; + else if (confirmation.success) success = true; + else if (confirmation.components) { notify = confirmation.components.notify.active; } } - if (confirmation.success) { - let dmd = false; - let dm; - try { - if (notify) { - dm = await (interaction.options.getMember("user") as GuildMember).send({ - embeds: [ - new EmojiEmbed() - .setEmoji("PUNISH.NICKNAME.RED") - .setTitle("Nickname changed") - .setDescription( - `Your nickname was ${ - interaction.options.getString("name") ? "changed" : "cleared" - } in ${interaction.guild.name}.` + - (interaction.options.getString("name") - ? ` it is now: ${interaction.options.getString("name")}` - : "") + - "\n\n" + - (confirmation.components.appeal.response - ? `You can appeal this here: <#${confirmation.components.appeal.response}>` - : "") - ) - .setStatus("Danger") - ] - }); - dmd = true; - } - } catch { - dmd = false; - } - try { - const member = interaction.options.getMember("user") as GuildMember; - const before = member.nickname; - const nickname = interaction.options.getString("name"); - member.setNickname(nickname ?? null, "Nucleus Nickname command"); - await client.database.history.create( - "nickname", - interaction.guild.id, - member.user, - interaction.user, - null, - before, - nickname - ); - const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger; - const data = { - meta: { - type: "memberUpdate", - displayName: "Member Updated", - calculateType: "guildMemberUpdate", - color: NucleusColors.yellow, - emoji: "PUNISH.NICKNAME.YELLOW", - timestamp: new Date().getTime() - }, - list: { - memberId: entry(member.id, `\`${member.id}\``), - before: entry(before, before ? before : "*None*"), - after: entry(nickname, nickname ? nickname : "*None*"), - updated: entry(new Date().getTime(), renderDelta(new Date().getTime())), - updatedBy: entry(interaction.user.id, renderUser(interaction.user)) - }, - hidden: { - guild: interaction.guild.id - } - }; - log(data); - } catch { - await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setEmoji("PUNISH.NICKNAME.RED") - .setTitle("Nickname") - .setDescription("Something went wrong and the users nickname could not be changed.") - .setStatus("Danger") - ], - components: [] - }); - if (dmd) await dm.delete(); - return; - } - const failed = !dmd && notify; - await interaction.editReply({ + if (timedOut) { + return await interaction.editReply({ embeds: [ new EmojiEmbed() - .setEmoji(`PUNISH.NICKNAME.${failed ? "YELLOW" : "GREEN"}`) + .setEmoji("PUNISH.NICKNAME.GREEN") .setTitle("Nickname") - .setDescription( - "The members nickname was changed" + - (failed ? ", but was not notified" : "") + - (confirmation.components.appeal.response - ? ` and an appeal ticket was opened in <#${confirmation.components.appeal.response}>` - : "") - ) - .setStatus(failed ? "Warning" : "Success") + .setDescription("No changes were made") + .setStatus("Success") ], components: [] }); - } else { + } + let dmd = false; + let dm; + try { + if (notify) { + dm = await (interaction.options.getMember("user") as GuildMember).send({ + embeds: [ + new EmojiEmbed() + .setEmoji("PUNISH.NICKNAME.RED") + .setTitle("Nickname changed") + .setDescription( + `Your nickname was ${interaction.options.getString("name") ? "changed" : "cleared"} in ${ + interaction.guild.name + }.` + + (interaction.options.getString("name") + ? ` it is now: ${interaction.options.getString("name")}` + : "") + + "\n\n" + + (confirmation.components.appeal.response + ? `You can appeal this here: <#${confirmation.components.appeal.response}>` + : "") + ) + .setStatus("Danger") + ] + }); + dmd = true; + } + } catch { + dmd = false; + } + try { + const member = interaction.options.getMember("user") as GuildMember; + const before = member.nickname; + const nickname = interaction.options.getString("name"); + member.setNickname(nickname ?? null, "Nucleus Nickname command"); + await client.database.history.create( + "nickname", + interaction.guild.id, + member.user, + interaction.user, + null, + before, + nickname + ); + const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger; + const data = { + meta: { + type: "memberUpdate", + displayName: "Member Updated", + calculateType: "guildMemberUpdate", + color: NucleusColors.yellow, + emoji: "PUNISH.NICKNAME.YELLOW", + timestamp: new Date().getTime() + }, + list: { + memberId: entry(member.id, `\`${member.id}\``), + before: entry(before, before ? before : "*None*"), + after: entry(nickname, nickname ? nickname : "*None*"), + updated: entry(new Date().getTime(), renderDelta(new Date().getTime())), + updatedBy: entry(interaction.user.id, renderUser(interaction.user)) + }, + hidden: { + guild: interaction.guild.id + } + }; + log(data); + } catch { await interaction.editReply({ embeds: [ new EmojiEmbed() - .setEmoji("PUNISH.NICKNAME.GREEN") + .setEmoji("PUNISH.NICKNAME.RED") .setTitle("Nickname") - .setDescription("No changes were made") - .setStatus("Success") + .setDescription("Something went wrong and the users nickname could not be changed.") + .setStatus("Danger") ], components: [] }); + if (dmd) await dm.delete(); + return; } + const failed = !dmd && notify; + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setEmoji(`PUNISH.NICKNAME.${failed ? "YELLOW" : "GREEN"}`) + .setTitle("Nickname") + .setDescription( + "The members nickname was changed" + + (failed ? ", but was not notified" : "") + + (confirmation.components.appeal.response + ? ` and an appeal ticket was opened in <#${confirmation.components.appeal.response}>` + : "") + ) + .setStatus(failed ? "Warning" : "Success") + ], + components: [] + }); }; const check = (interaction: CommandInteraction) => { diff --git a/src/commands/mod/purge.ts b/src/commands/mod/purge.ts index 24fea75..2978cc1 100644 --- a/src/commands/mod/purge.ts +++ b/src/commands/mod/purge.ts @@ -60,7 +60,9 @@ const callback = async (interaction: CommandInteraction): Promise => { fetchReply: true }); let deleted = [] as Discord.Message[]; - while (true) { + let timedOut = false; + let amountSelected = false; + while (!timedOut && !amountSelected) { const m = (await interaction.editReply({ embeds: [ new EmojiEmbed() @@ -98,16 +100,16 @@ const callback = async (interaction: CommandInteraction): Promise => { time: 300000 }); } catch (e) { - break; + timedOut = true; + continue; } component.deferUpdate(); - if (component.customId === "done") break; - let amount; - try { - amount = parseInt(component.customId); - } catch { - break; + if (component.customId === "done") { + amountSelected = true; + continue; } + const amount = parseInt(component.customId); + let messages; await (interaction.channel as TextChannel).messages.fetch({ limit: amount }).then(async (ms) => { if (user) { diff --git a/src/commands/mod/softban.ts b/src/commands/mod/softban.ts index 67ecdd6..463ec16 100644 --- a/src/commands/mod/softban.ts +++ b/src/commands/mod/softban.ts @@ -26,7 +26,9 @@ const callback = async (interaction: CommandInteraction): Promise => { let reason = null; let notify = true; let confirmation; - while (true) { + let timedOut = false; + let success = false; + while (!timedOut && !success) { const confirmation = await new confirmationMessage(interaction) .setEmoji("PUNISH.BAN.RED") .setTitle("Softban") @@ -55,13 +57,14 @@ const callback = async (interaction: CommandInteraction): Promise => { .addReasonButton(reason ?? "") .send(reason !== null); reason = reason ?? ""; - if (confirmation.cancelled) return; - if (confirmation.success) break; - if (confirmation.newReason) reason = confirmation.newReason; - if (confirmation.components) { + if (confirmation.cancelled) timedOut = true; + else if (confirmation.success) success = true; + else if (confirmation.newReason) reason = confirmation.newReason; + else if (confirmation.components) { notify = confirmation.components.notify.active; } } + if (timedOut) return; if (confirmation.success) { let dmd = false; const config = await client.database.guilds.read(interaction.guild.id); diff --git a/src/commands/mod/unmute.ts b/src/commands/mod/unmute.ts index 18363d5..fbe6d66 100644 --- a/src/commands/mod/unmute.ts +++ b/src/commands/mod/unmute.ts @@ -17,7 +17,8 @@ const callback = async (interaction: CommandInteraction): Promise => { let reason = null; let notify = false; let confirmation; - while (true) { + let success = false; + while (!success) { confirmation = await new confirmationMessage(interaction) .setEmoji("PUNISH.MUTE.RED") .setTitle("Unmute") @@ -32,9 +33,9 @@ const callback = async (interaction: CommandInteraction): Promise => { .setColor("Danger") .addReasonButton(reason ?? "") .send(reason !== null); - if (confirmation.success) break; - if (confirmation.newReason) reason = confirmation.newReason; - if (confirmation.components) { + if (confirmation.success) success = true; + else if (confirmation.newReason) reason = confirmation.newReason; + else if (confirmation.components) { notify = confirmation.components.notify.active; } } diff --git a/src/commands/mod/viewas.ts b/src/commands/mod/viewas.ts index 1120f72..ba9bc1d 100644 --- a/src/commands/mod/viewas.ts +++ b/src/commands/mod/viewas.ts @@ -56,7 +56,8 @@ const callback = async (interaction: CommandInteraction): Promise => { fetchReply: true }); let page = 0; - while (true) { + let timedOut = false; + while (!timedOut) { m = await interaction.editReply({ embeds: [ new EmojiEmbed() @@ -144,7 +145,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - return; + timedOut = true; + continue; } i.deferUpdate(); if (i.customId === "next") { diff --git a/src/commands/mod/warn.ts b/src/commands/mod/warn.ts index 87e6e07..50480fa 100644 --- a/src/commands/mod/warn.ts +++ b/src/commands/mod/warn.ts @@ -19,14 +19,16 @@ const callback = async (interaction: CommandInteraction): Promise => { let notify = true; let createAppealTicket = false; let confirmation; - while (true) { + let timedOut = false; + let success = false; + while (!timedOut && !success) { confirmation = await new confirmationMessage(interaction) .setEmoji("PUNISH.WARN.RED") .setTitle("Warn") .setDescription( keyValueList({ user: renderUser(interaction.options.getUser("user")), - reason: reason ? "\n> " + (reason ?? "").replaceAll("\n", "\n> ") : "*No reason provided*" + reason: reason ? "\n> " + reason.replaceAll("\n", "\n> ") : "*No reason provided*" }) + `The user **will${notify ? "" : " not"}** be notified\n\n` + `Are you sure you want to warn <@!${(interaction.options.getMember("user") as GuildMember).id}>?` @@ -54,14 +56,15 @@ const callback = async (interaction: CommandInteraction): Promise => { .addReasonButton(reason ?? "") .send(reason !== null); reason = reason ?? ""; - if (confirmation.cancelled) return; - if (confirmation.success) break; - if (confirmation.newReason) reason = confirmation.newReason; - if (confirmation.components) { + if (confirmation.cancelled) timedOut = true; + else if (confirmation.success) success = true; + else if (confirmation.newReason) reason = confirmation.newReason; + else if (confirmation.components) { notify = confirmation.components.notify.active; createAppealTicket = confirmation.components.appeal.active; } } + if (timedOut) return; if (confirmation.success) { let dmd = false; try { @@ -284,11 +287,10 @@ const callback = async (interaction: CommandInteraction): Promise => { const check = (interaction: CommandInteraction) => { const member = interaction.member as GuildMember; - const me = interaction.guild.me!; - const apply = interaction.options.getMember("user") as GuildMember; - if (member === null || me === null || apply === null) throw new Error("That member is not in the server"); - const memberPos = member.roles ? member.roles.highest.position : 0; - const applyPos = apply.roles ? apply.roles.highest.position : 0; + const apply = interaction.options.getMember("user") as GuildMember | null; + if (apply === null) throw new Error("That member is not in the server"); + const memberPos = member.roles.cache.size ? member.roles.highest.position : 0; + const applyPos = apply.roles.cache.size ? apply.roles.highest.position : 0; // Do not allow warning bots if (member.user.bot) throw new Error("I cannot warn bots"); // Allow the owner to warn anyone diff --git a/src/commands/privacy.ts b/src/commands/privacy.ts index 4de0c63..9f63602 100644 --- a/src/commands/privacy.ts +++ b/src/commands/privacy.ts @@ -122,7 +122,8 @@ const callback = async (interaction: CommandInteraction): Promise => { let selectPaneOpen = false; let nextFooter = null; - while (true) { + let timedOut = false; + while (!timedOut) { let selectPane = []; if (selectPaneOpen) { @@ -176,7 +177,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - break; + timedOut = true; + continue; } nextFooter = null; i.deferUpdate(); diff --git a/src/commands/role/user.ts b/src/commands/role/user.ts index 736e247..1b91b71 100644 --- a/src/commands/role/user.ts +++ b/src/commands/role/user.ts @@ -94,8 +94,8 @@ const callback = async (interaction: CommandInteraction): Promise => { const check = (interaction: CommandInteraction, _defaultCheck: WrappedCheck) => { const member = interaction.member as GuildMember; const me = interaction.guild.me!; - const apply = interaction.options.getMember("user") as GuildMember; - if (member === null || me === null || apply === null) throw new Error("That member is not in the server"); + const apply = interaction.options.getMember("user") as GuildMember | null; + if (apply === null) throw new Error("That member is not in the server"); // Check if Nucleus has permission to role if (!me.permissions.has("MANAGE_ROLES")) throw new Error("I do not have the *Manage Roles* permission"); // Allow the owner to role anyone diff --git a/src/commands/settings/commands.ts b/src/commands/settings/commands.ts index fe6a181..294c0fd 100644 --- a/src/commands/settings/commands.ts +++ b/src/commands/settings/commands.ts @@ -50,7 +50,8 @@ const callback = async (interaction: CommandInteraction): Promise => { }); } } - while (true) { + let timedOut = false; + while (!timedOut) { const config = await client.database.guilds.read(interaction.guild.id); const moderation = config.getKey("moderation"); m = await interaction.editReply({ @@ -119,7 +120,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - return; + timedOut = true; + continue; } let chosen = moderation[i.customId] ?? { text: null, url: null }; if (i.component.customId === "clearMuteRole") { diff --git a/src/commands/settings/logs/attachment.ts b/src/commands/settings/logs/attachment.ts index 52fdd5f..843b391 100644 --- a/src/commands/settings/logs/attachment.ts +++ b/src/commands/settings/logs/attachment.ts @@ -117,7 +117,9 @@ const callback = async (interaction: CommandInteraction): Promise => { let clicks = 0; const data = await client.database.guilds.read(interaction.guild.id); let channel = data.logging.staff.channel; - while (true) { + + let timedOut = false; + while (!timedOut) { await interaction.editReply({ embeds: [ new EmojiEmbed() @@ -148,7 +150,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - break; + timedOut = true; + continue; } i.deferUpdate(); if (i.component.customId === "clear") { @@ -158,8 +161,6 @@ const callback = async (interaction: CommandInteraction): Promise => { await client.database.guilds.write(interaction.guild.id, null, ["logging.announcements.channel"]); channel = undefined; } - } else { - break; } } await interaction.editReply({ diff --git a/src/commands/settings/logs/channel.ts b/src/commands/settings/logs/channel.ts index c645581..206d282 100644 --- a/src/commands/settings/logs/channel.ts +++ b/src/commands/settings/logs/channel.ts @@ -114,7 +114,8 @@ const callback = async (interaction: CommandInteraction): Promise => { let clicks = 0; const data = await client.database.guilds.read(interaction.guild.id); let channel = data.logging.logs.channel; - while (true) { + let timedOut = false; + while (!timedOut) { await interaction.editReply({ embeds: [ new EmojiEmbed() @@ -142,7 +143,7 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - break; + timedOut = true; } i.deferUpdate(); if (i.component.customId === "clear") { @@ -152,8 +153,6 @@ const callback = async (interaction: CommandInteraction): Promise => { await client.database.guilds.write(interaction.guild.id, null, ["logging.logs.channel"]); channel = undefined; } - } else { - break; } } await interaction.editReply({ diff --git a/src/commands/settings/logs/events.ts b/src/commands/settings/logs/events.ts index c3fbe41..d0db316 100644 --- a/src/commands/settings/logs/events.ts +++ b/src/commands/settings/logs/events.ts @@ -1,5 +1,5 @@ import { LoadingEmbed } from "./../../../utils/defaultEmbeds.js"; -import Discord, { CommandInteraction, MessageActionRow, MessageButton, MessageSelectMenu } from "discord.js"; +import Discord, { CommandInteraction, Message, MessageActionRow, MessageButton, MessageSelectMenu } from "discord.js"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { WrappedCheck } from "jshaiku"; import EmojiEmbed from "../../../utils/generateEmojiEmbed.js"; @@ -38,11 +38,12 @@ const callback = async (interaction: CommandInteraction): Promise => { fetchReply: true, ephemeral: true }); - let m; - while (true) { + let m: Message; + let timedOut = false; + do { const config = await client.database.guilds.read(interaction.guild.id); const converted = toHexArray(config.logging.logs.toLog); - m = await interaction.editReply({ + m = (await interaction.editReply({ embeds: [ new EmojiEmbed() .setTitle("Logging Events") @@ -72,12 +73,13 @@ const callback = async (interaction: CommandInteraction): Promise => { new MessageButton().setLabel("Select none").setStyle("DANGER").setCustomId("none") ]) ] - }); + })) as Message; let i; try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - break; + timedOut = true; + continue; } i.deferUpdate(); if (i.customId === "logs") { @@ -95,22 +97,10 @@ const callback = async (interaction: CommandInteraction): Promise => { await client.database.guilds.write(interaction.guild.id, { "logging.logs.toLog": 0 }); - } else { - break; } - } - m = await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setTitle("Logging Events") - .setDescription( - "Below are the events being logged in the server. You can toggle them on and off in the dropdown" - ) - .setFooter({ text: "Message timed out" }) - .setStatus("Success") - .setEmoji("CHANNEL.TEXT.CREATE") - ] - }); + } while (!timedOut); + + await interaction.editReply({ embeds: [m.embeds[0]!.setFooter({ text: "Message timed out" })] }); return; }; diff --git a/src/commands/settings/logs/staff.ts b/src/commands/settings/logs/staff.ts index 715fbea..c7077cf 100644 --- a/src/commands/settings/logs/staff.ts +++ b/src/commands/settings/logs/staff.ts @@ -119,7 +119,8 @@ const callback = async (interaction: CommandInteraction): Promise => { let clicks = 0; const data = await client.database.guilds.read(interaction.guild.id); let channel = data.logging.staff.channel; - while (true) { + let timedOut = false; + while (!timedOut) { await interaction.editReply({ embeds: [ new EmojiEmbed() @@ -147,7 +148,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - break; + timedOut = true; + continue; } i.deferUpdate(); if ((i.component as MessageButton).customId === "clear") { @@ -157,8 +159,6 @@ const callback = async (interaction: CommandInteraction): Promise => { await client.database.guilds.write(interaction.guild.id, null, ["logging.staff.channel"]); channel = undefined; } - } else { - break; } } await interaction.editReply({ diff --git a/src/commands/settings/stats.ts b/src/commands/settings/stats.ts index b4bca10..ab6022e 100644 --- a/src/commands/settings/stats.ts +++ b/src/commands/settings/stats.ts @@ -148,7 +148,8 @@ const callback = async (interaction: CommandInteraction): Promise => { } await statsChannelAddCallback(client, interaction.member); } - while (true) { + let timedOut = false; + while (!timedOut) { config = await client.database.guilds.read(interaction.guild.id); const stats = config.getKey("stats"); const selectMenu = new MessageSelectMenu() @@ -198,7 +199,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - break; + timedOut = true; + continue; } i.deferUpdate(); if (i.customId === "remove") { @@ -211,7 +213,7 @@ const callback = async (interaction: CommandInteraction): Promise => { } } await interaction.editReply({ - embeds: [m.embeds[0]!.setFooter({ text: "Message closed" })], + embeds: [m.embeds[0]!.setFooter({ text: "Message timed out" })], components: [] }); }; diff --git a/src/commands/settings/tickets.ts b/src/commands/settings/tickets.ts index ca170be..26fa66e 100644 --- a/src/commands/settings/tickets.ts +++ b/src/commands/settings/tickets.ts @@ -67,13 +67,12 @@ const callback = async (interaction: CommandInteraction): Promise => { fetchReply: true })) as Message; const options = { - enabled: interaction.options.getString("enabled") as string | boolean | null, + enabled: interaction.options.getString("enabled")?.startsWith("yes") as boolean | null, category: interaction.options.getChannel("category"), maxtickets: interaction.options.getNumber("maxticketsperuser"), supportping: interaction.options.getRole("supportrole") }; if (options.enabled !== null || options.category || options.maxtickets || options.supportping) { - options.enabled = options.enabled === "yes" ? true : false; if (options.category) { let channel: GuildChannel | null; try { @@ -211,7 +210,8 @@ const callback = async (interaction: CommandInteraction): Promise => { types: data.tickets.types, customTypes: data.tickets.customTypes }; - while (true) { + let timedOut = false; + while (!timedOut) { embed = new EmojiEmbed() .setTitle("Tickets") .setDescription( @@ -276,7 +276,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - break; + timedOut = true; + continue; } i.deferUpdate(); if ((i.component as MessageActionRowComponent).customId === "clearCategory") { @@ -312,7 +313,9 @@ const callback = async (interaction: CommandInteraction): Promise => { description: "Click the button below to speak to us privately" } ]; - while (true) { + let innerTimedOut = false; + let templateSelected = false; + while (!innerTimedOut && !templateSelected) { const enabled = data.enabled && data.category !== null; await interaction.editReply({ embeds: [ @@ -373,7 +376,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - break; + innerTimedOut = true; + continue; } if ((i.component as MessageActionRowComponent).customId === "template") { i.deferUpdate(); @@ -397,7 +401,8 @@ const callback = async (interaction: CommandInteraction): Promise => { ]) ] }); - break; + templateSelected = true; + continue; } else if ((i.component as MessageActionRowComponent).customId === "blank") { i.deferUpdate(); await interaction.channel!.send({ @@ -411,7 +416,8 @@ const callback = async (interaction: CommandInteraction): Promise => { ]) ] }); - break; + templateSelected = true; + continue; } else if ((i.component as MessageActionRowComponent).customId === "custom") { await i.showModal( new Discord.Modal() @@ -462,7 +468,8 @@ const callback = async (interaction: CommandInteraction): Promise => { (m) => m.customId === "modify" ); } catch (e) { - break; + innerTimedOut = true; + continue; } if (out.fields) { const title = out.fields.getTextInputValue("title"); @@ -485,9 +492,7 @@ const callback = async (interaction: CommandInteraction): Promise => { ]) ] }); - break; - } else { - continue; + templateSelected = true; } } } @@ -498,18 +503,18 @@ const callback = async (interaction: CommandInteraction): Promise => { data.enabled = !data.enabled; } else if ((i.component as MessageActionRowComponent).customId === "manageTypes") { data = await manageTypes(interaction, data, m as Message); - } else { - break; } } await interaction.editReply({ - embeds: [embed.setFooter({ text: "Message closed" })], + embeds: [embed.setFooter({ text: "Message timed out" })], components: [] }); }; async function manageTypes(interaction: CommandInteraction, data: GuildConfig["tickets"], m: Message) { - while (true) { + let timedOut = false; + let backPressed = false; + while (!timedOut && !backPressed) { if (data.useCustom) { const customTypes = data.customTypes; await interaction.editReply({ @@ -622,7 +627,8 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - break; + timedOut = true; + continue; } if (i.component.customId === "types") { i.deferUpdate(); @@ -700,7 +706,7 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t } catch { continue; } - data.customTypes = data.customTypes || []; + data.customTypes = data.customTypes ?? []; if (!data.customTypes.includes(toAdd)) { data.customTypes.push(toAdd); } @@ -717,7 +723,7 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t data.useCustom = true; } else { i.deferUpdate(); - break; + backPressed = true; } } return data; diff --git a/src/commands/settings/verify.ts b/src/commands/settings/verify.ts index 0eb4553..aa8227f 100644 --- a/src/commands/settings/verify.ts +++ b/src/commands/settings/verify.ts @@ -124,7 +124,9 @@ const callback = async (interaction: CommandInteraction): Promise => { let clicks = 0; const data = await client.database.guilds.read(interaction.guild!.id); let role = data.verify.role; - while (true) { + + let timedOut = false; + while (!timedOut) { await interaction.editReply({ embeds: [ new EmojiEmbed() @@ -155,7 +157,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - break; + timedOut = true; + continue; } i.deferUpdate(); if ((i.component as MessageActionRowComponent).customId === "clear") { @@ -180,7 +183,9 @@ const callback = async (interaction: CommandInteraction): Promise => { description: "Click the button below to verify yourself" } ]; - while (true) { + let innerTimedOut = false; + let templateSelected = false; + while (!innerTimedOut && !templateSelected) { await interaction.editReply({ embeds: [ new EmojiEmbed() @@ -238,7 +243,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - break; + innerTimedOut = true; + continue; } if ((i.component as MessageActionRowComponent).customId === "template") { i.deferUpdate(); @@ -262,7 +268,8 @@ const callback = async (interaction: CommandInteraction): Promise => { ]) ] }); - break; + templateSelected = true; + continue; } else if ((i.component as MessageActionRowComponent).customId === "blank") { i.deferUpdate(); await interaction.channel!.send({ @@ -276,7 +283,8 @@ const callback = async (interaction: CommandInteraction): Promise => { ]) ] }); - break; + templateSelected = true; + continue; } else if ((i.component as MessageActionRowComponent).customId === "custom") { await i.showModal( new Discord.Modal() @@ -329,11 +337,10 @@ const callback = async (interaction: CommandInteraction): Promise => { (m) => m.customId === "modify" ); } catch (e) { - break; - } - if (out === null) { + innerTimedOut = true; continue; - } else if (out instanceof ModalSubmitInteraction) { + } + if (out !== null && out instanceof ModalSubmitInteraction) { const title = out.fields.getTextInputValue("title"); const description = out.fields.getTextInputValue("description"); await interaction.channel!.send({ @@ -354,9 +361,7 @@ const callback = async (interaction: CommandInteraction): Promise => { ]) ] }); - break; - } else { - continue; + templateSelected = true; } } } diff --git a/src/commands/settings/welcome.ts b/src/commands/settings/welcome.ts index 188e4b9..24ccefc 100644 --- a/src/commands/settings/welcome.ts +++ b/src/commands/settings/welcome.ts @@ -172,7 +172,8 @@ const callback = async (interaction: CommandInteraction): Promise => { } } let lastClicked = null; - while (true) { + let timedOut = false; + do { const config = await client.database.guilds.read(interaction.guild!.id); m = (await interaction.editReply({ embeds: [ @@ -239,7 +240,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - break; + timedOut = true; + continue; } i.deferUpdate(); if (i.customId == "clear-message") { @@ -284,9 +286,9 @@ const callback = async (interaction: CommandInteraction): Promise => { }); lastClicked = null; } - } + } while (!timedOut); await interaction.editReply({ - embeds: [m.embeds[0]!.setFooter({ text: "Message closed" })], + embeds: [m.embeds[0]!.setFooter({ text: "Message timed out" })], components: [] }); }; diff --git a/src/commands/user/about.ts b/src/commands/user/about.ts index 6ae3231..4701630 100644 --- a/src/commands/user/about.ts +++ b/src/commands/user/about.ts @@ -7,9 +7,10 @@ import Discord, { MessageActionRowComponent, MessageButton, MessageComponentInteraction, + MessageSelectOptionData, SelectMenuInteraction } from "discord.js"; -import { SelectMenuOption, SlashCommandSubcommandBuilder } from "@discordjs/builders"; +import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import getEmojiByName from "../../utils/getEmojiByName.js"; import generateKeyValueList from "../../utils/generateKeyValueList.js"; @@ -224,22 +225,20 @@ const callback = async (interaction: CommandInteraction): Promise => { ephemeral: true })) as Message; let page = 0; - let breakReason = ""; - while (true) { + let timedOut = false; + while (!timedOut) { const em = new Discord.MessageEmbed(embeds[page].embed); em.setDescription(em.description + "\n" + createPageIndicator(embeds.length, page)); let selectPane = []; if (selectPaneOpen) { - const options = []; + const options: MessageSelectOptionData[] = []; embeds.forEach((embed) => { - options.push( - new SelectMenuOption({ - label: embed.title, - value: embed.pageId.toString(), - description: embed.description || "" - }) - ); + options.push({ + label: embed.title, + value: embed.pageId.toString(), + description: embed.description || "" + }); }); selectPane = [ new MessageActionRow().addComponents([ @@ -269,11 +268,7 @@ const callback = async (interaction: CommandInteraction): Promise => { .setEmoji(getEmojiByName("CONTROL.RIGHT", "id")) .setCustomId("right") .setStyle("SECONDARY") - .setDisabled(page === embeds.length - 1), - new MessageButton() - .setEmoji(getEmojiByName("CONTROL.CROSS", "id")) - .setCustomId("close") - .setStyle("DANGER") + .setDisabled(page === embeds.length - 1) ]) ]) }); @@ -281,8 +276,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { i = await m.awaitMessageComponent({ time: 300000 }); } catch { - breakReason = "Message timed out"; - break; + timedOut = true; + continue; } i.deferUpdate(); if ((i.component as MessageActionRowComponent).customId === "left") { @@ -293,45 +288,16 @@ const callback = async (interaction: CommandInteraction): Promise => { selectPaneOpen = false; } else if ((i.component as MessageActionRowComponent).customId === "select") { selectPaneOpen = !selectPaneOpen; - } else if ((i.component as MessageActionRowComponent).customId === "close") { - breakReason = "Message closed"; - break; } else if ((i.component as MessageActionRowComponent).customId === "page") { page = parseInt((i as SelectMenuInteraction).values[0]); selectPaneOpen = false; - } else { - breakReason = "Message closed"; - break; } } const em = new Discord.MessageEmbed(embeds[page].embed); - em.setDescription(em.description + "\n" + createPageIndicator(embeds.length, page) + " | " + breakReason); + em.setDescription(em.description + "\n" + createPageIndicator(embeds.length, page) + " | Message closed"); await interaction.editReply({ embeds: [em], - components: [ - new MessageActionRow().addComponents([ - new MessageButton() - .setEmoji(getEmojiByName("CONTROL.LEFT", "id")) - .setStyle("SECONDARY") - .setCustomId("left") - .setDisabled(true), - new MessageButton() - .setEmoji(getEmojiByName("CONTROL.MENU", "id")) - .setStyle("SECONDARY") - .setCustomId("select") - .setDisabled(true), - new MessageButton() - .setEmoji(getEmojiByName("CONTROL.RIGHT", "id")) - .setCustomId("right") - .setStyle("SECONDARY") - .setDisabled(true), - new MessageButton() - .setEmoji(getEmojiByName("CONTROL.CROSS", "id")) - .setCustomId("close") - .setStyle("DANGER") - .setDisabled(true) - ]) - ] + components: [] }); }; diff --git a/src/commands/user/track.ts b/src/commands/user/track.ts index da62880..8c991e3 100644 --- a/src/commands/user/track.ts +++ b/src/commands/user/track.ts @@ -35,7 +35,8 @@ const callback = async (interaction: CommandInteraction): Promise => { const roles = await guild.roles.fetch(); const memberRoles = member.roles; let managed: boolean; - while (true) { + let timedOut = false; + while (!timedOut) { const data = config.tracks[track]; if (data.manageableBy !== undefined) managed = data.manageableBy.some((element: string) => { @@ -169,7 +170,8 @@ const callback = async (interaction: CommandInteraction): Promise => { try { component = await m.awaitMessageComponent({ time: 300000 }); } catch (e) { - return; + timedOut = true; + continue; } component.deferUpdate(); if (component.customId === "conflict") { diff --git a/src/utils/database.ts b/src/utils/database.ts index cda63d6..2c299d0 100644 --- a/src/utils/database.ts +++ b/src/utils/database.ts @@ -34,13 +34,13 @@ export class Guilds { this.defaultData = null; } - async setup() { + async setup(): Promise { this.defaultData = (await import("../config/default.json", { assert: { type: "json" } })) .default as unknown as GuildConfig; return this; } - async read(guild: string) { + async read(guild: string): Promise { const entry = await this.guilds.findOne({ id: guild }); return new Proxy(structuredClone(this.defaultData), Entry(entry)) as unknown as GuildConfig; } @@ -242,10 +242,6 @@ export interface GuildConfig { }; welcome: { enabled: boolean; - verificationRequired: { - message: boolean; - role: string | null; - }; role: string | null; ping: string | null; channel: string | null; @@ -256,7 +252,7 @@ export interface GuildConfig { logs: { enabled: boolean; channel: string | null; - toLog: string | null; + toLog: string; }; staff: { channel: string | null; @@ -267,14 +263,13 @@ export interface GuildConfig { }; }; verify: { - enabled: boolean; role: string | null; }; tickets: { enabled: boolean; category: string | null; - types: string | null; - customTypes: string[]; + types: string; + customTypes: string[] | null; useCustom: boolean; supportRole: string | null; maxTickets: number;