From 9b2ac4d68d9631c097858c01e3e5aac584ac63a6 Mon Sep 17 00:00:00 2001 From: PineaFan Date: Wed, 18 Jan 2023 14:41:07 +0000 Subject: [PATCH] fixed up a few commands --- src/actions/createModActionTicket.ts | 3 +- src/actions/tickets/delete.ts | 79 ++++++++++++++++------------ src/commands/mod/ban.ts | 2 +- src/commands/mod/kick.ts | 2 +- src/commands/mod/mute.ts | 2 +- src/commands/mod/softban.ts | 2 +- src/commands/mod/warn.ts | 2 +- src/commands/settings/commands.ts | 37 ++++++------- src/commands/settings/stats.ts | 7 +-- src/config/emojis.json | 4 -- src/events/channelDelete.ts | 43 ++++++++++----- src/events/commandError.ts | 9 ++-- src/utils/log.ts | 2 +- tsconfig.json | 2 +- 14 files changed, 111 insertions(+), 85 deletions(-) diff --git a/src/actions/createModActionTicket.ts b/src/actions/createModActionTicket.ts index 61a94ba..bdc5e07 100644 --- a/src/actions/createModActionTicket.ts +++ b/src/actions/createModActionTicket.ts @@ -1,3 +1,4 @@ +import { getCommandMentionByName } from './../utils/getCommandMentionByName.js'; import Discord, { ActionRowBuilder, ButtonBuilder, OverwriteType, ChannelType, ButtonStyle } from "discord.js"; import EmojiEmbed from "../utils/generateEmojiEmbed.js"; import getEmojiByName from "../utils/getEmojiByName.js"; @@ -62,7 +63,7 @@ export async function create( `**Support type:** ${customReason ? customReason : "Appeal submission"}\n` + (reason !== null ? `**Reason:**\n> ${reason}\n` : "") + `**Ticket ID:** \`${c.id}\`\n` + - "Type `/ticket close` to close this ticket." + `Type ${getCommandMentionByName("ticket/close")} to close this ticket.` ) .setStatus("Success") .setEmoji("GUILD.TICKET.OPEN") diff --git a/src/actions/tickets/delete.ts b/src/actions/tickets/delete.ts index 48d2a51..3263580 100644 --- a/src/actions/tickets/delete.ts +++ b/src/actions/tickets/delete.ts @@ -1,5 +1,5 @@ import { getCommandMentionByName } from '../../utils/getCommandMentionByName.js'; -import Discord, { ActionRowBuilder, ButtonBuilder, ButtonInteraction, PrivateThreadChannel, TextChannel, ButtonStyle } from "discord.js"; +import Discord, { ActionRowBuilder, ButtonBuilder, ButtonInteraction, PrivateThreadChannel, TextChannel, ButtonStyle, CategoryChannel } from "discord.js"; import client from "../../utils/client.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import getEmojiByName from "../../utils/getEmojiByName.js"; @@ -142,47 +142,60 @@ export default async function (interaction: Discord.CommandInteraction | ButtonI async function purgeByUser(member: string, guild: string) { - const config = await client.database.guilds.read(guild.id); + const config = await client.database.guilds.read(guild); const fetchedGuild = await client.guilds.fetch(guild); if (!config.tickets.category) return; - const tickets = fetchedGuild.channels.cache.get(config.tickets.category); + const tickets: CategoryChannel | TextChannel | undefined = fetchedGuild.channels.cache.get(config.tickets.category) as CategoryChannel | TextChannel | undefined; if (!tickets) return; - const ticketChannels = tickets.children; let deleted = 0; - ticketChannels.forEach((element) => { - if (element.type !== "GUILD_TEXT") return; - if (element.topic.split(" ")[0] === member) { + if (tickets.type === Discord.ChannelType.GuildCategory) { + // For channels, the topic is the user ID, then the word Active + const category = tickets as Discord.CategoryChannel; + category.children.cache.forEach((element) => { + if (!(element.type === Discord.ChannelType.GuildText)) return; + if (!(((element as Discord.TextChannel).topic ?? "").includes(member))) return; try { element.delete(); - } catch { - /* Errors if the channel does not exist (deleted already) */ + deleted++; + } catch (e) { + console.error(e); } - deleted++; - } - }); - if (deleted) { - const { log, NucleusColors, entry, renderUser, renderDelta } = member.client.logger; - const data = { - meta: { - type: "ticketPurge", - displayName: "Tickets Purged", - calculateType: "ticketUpdate", - color: NucleusColors.red, - emoji: "GUILD.TICKET.DELETE", - timestamp: new Date().getTime() - }, - list: { - ticketFor: entry(member, renderUser(member)), - deletedBy: entry(null, "Member left server"), - deleted: entry(new Date().getTime(), renderDelta(new Date().getTime())), - ticketsDeleted: deleted - }, - hidden: { - guild: guild.id + }); + } else { + // For threads, the name is the users name, id, then the word Active + const channel = tickets as Discord.TextChannel; + channel.threads.cache.forEach((element: Discord.ThreadChannel) => { + if (!element.name.includes(member)) return; + try { + element.delete(); + deleted++; + } catch (e) { + console.error(e); } - }; - log(data); + }); } + if (!deleted) return + const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger; + const data = { + meta: { + type: "ticketPurge", + displayName: "Tickets Purged", + calculateType: "ticketUpdate", + color: NucleusColors.red, + emoji: "GUILD.TICKET.DELETE", + timestamp: new Date().getTime() + }, + list: { + ticketFor: entry(member, renderUser(member)), + deletedBy: entry(null, "Member left server"), + deleted: entry(new Date().getTime(), renderDelta(new Date().getTime())), + ticketsDeleted: deleted + }, + hidden: { + guild: guild + } + }; + log(data); } export { purgeByUser }; \ No newline at end of file diff --git a/src/commands/mod/ban.ts b/src/commands/mod/ban.ts index 18b6c7e..70e904c 100644 --- a/src/commands/mod/ban.ts +++ b/src/commands/mod/ban.ts @@ -97,7 +97,7 @@ const callback = async (interaction: CommandInteraction): Promise => { .addComponents(new ButtonBuilder() .setStyle(ButtonStyle.Link) .setLabel(config.moderation.ban.text) - .setURL(config.moderation.ban.link) + .setURL(config.moderation.ban.link.replaceAll("{id}", (interaction.options.getMember("user") as GuildMember).id)) ) ) } diff --git a/src/commands/mod/kick.ts b/src/commands/mod/kick.ts index bdbb9ee..380bcc9 100644 --- a/src/commands/mod/kick.ts +++ b/src/commands/mod/kick.ts @@ -86,7 +86,7 @@ const callback = async (interaction: CommandInteraction): Promise => { .addComponents(new ButtonBuilder() .setStyle(ButtonStyle.Link) .setLabel(config.moderation.kick.text) - .setURL(config.moderation.kick.link) + .setURL(config.moderation.kick.link.replaceAll("{id}", (interaction.options.getMember("user") as GuildMember).id)) ) ) } diff --git a/src/commands/mod/mute.ts b/src/commands/mod/mute.ts index 3270d37..86291e5 100644 --- a/src/commands/mod/mute.ts +++ b/src/commands/mod/mute.ts @@ -252,7 +252,7 @@ const callback = async (interaction: CommandInteraction): Promise => { .addComponents(new ButtonBuilder() .setStyle(ButtonStyle.Link) .setLabel(config.moderation.mute.text) - .setURL(config.moderation.mute.link) + .setURL(config.moderation.mute.link.replaceAll("{id}", (interaction.options.getMember("user") as GuildMember).id)) ) ) }; diff --git a/src/commands/mod/softban.ts b/src/commands/mod/softban.ts index 35f275f..2787e91 100644 --- a/src/commands/mod/softban.ts +++ b/src/commands/mod/softban.ts @@ -97,7 +97,7 @@ const callback = async (interaction: CommandInteraction): Promise => { .addComponents(new ButtonBuilder() .setStyle(ButtonStyle.Link) .setLabel(config.moderation.softban.text) - .setURL(config.moderation.softban.link) + .setURL(config.moderation.softban.link.replaceAll("{id}", (interaction.options.getMember("user") as GuildMember).id)) ) ) } diff --git a/src/commands/mod/warn.ts b/src/commands/mod/warn.ts index 93241e1..8e96078 100644 --- a/src/commands/mod/warn.ts +++ b/src/commands/mod/warn.ts @@ -99,7 +99,7 @@ const callback = async (interaction: CommandInteraction): Promise => { .addComponents(new ButtonBuilder() .setStyle(ButtonStyle.Link) .setLabel(config.moderation.warn.text) - .setURL(config.moderation.warn.link) + .setURL(config.moderation.warn.link.replaceAll("{id}", (interaction.options.getMember("user") as GuildMember).id)) ) ) } diff --git a/src/commands/settings/commands.ts b/src/commands/settings/commands.ts index ac2cd81..25034b2 100644 --- a/src/commands/settings/commands.ts +++ b/src/commands/settings/commands.ts @@ -1,5 +1,5 @@ import { LoadingEmbed } from "../../utils/defaults.js"; -import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, TextInputComponent, Role, ButtonStyle, ButtonComponent, TextInputBuilder } from "discord.js"; +import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, Role, ButtonStyle, ButtonComponent, TextInputBuilder } from "discord.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import getEmojiByName from "../../utils/getEmojiByName.js"; import type { SlashCommandSubcommandBuilder } from "@discordjs/builders"; @@ -43,7 +43,7 @@ const callback = async (interaction: CommandInteraction): Promise => { let timedOut = false; while (!timedOut) { const config = await client.database.guilds.read(interaction.guild!.id); - const moderation = config["moderation"]; + const moderation = config.moderation; m = await interaction.editReply({ embeds: [ new EmojiEmbed() @@ -117,7 +117,7 @@ const callback = async (interaction: CommandInteraction): Promise => { continue; } type modIDs = "mute" | "kick" | "ban" | "softban" | "warn" | "role"; - let chosen = moderation[i.customId as modIDs] ?? { text: null, url: null }; + let chosen = moderation[i.customId as modIDs]; if ((i.component as ButtonComponent).customId === "clearMuteRole") { i.deferUpdate(); if (clicked === "clearMuteRole") { @@ -181,31 +181,28 @@ const callback = async (interaction: CommandInteraction): Promise => { ]) ] }); - let out: Discord.ModalSubmitInteraction; + let out: Discord.ModalSubmitInteraction | null; try { out = await modalInteractionCollector( m, (m) => m.channel!.id === interaction.channel!.id, (_) => true - ) as Discord.ModalSubmitInteraction; + ) as Discord.ModalSubmitInteraction | null; } catch (e) { continue; } - if ((out!).fields) { - const buttonText = out.fields.getTextInputValue("name"); - const buttonLink = out.fields.getTextInputValue("url").replace(/{id}/gi, "{id}"); - const current = chosen; - if (current.text !== buttonText || current.link !== buttonLink) { - chosen = { text: buttonText, link: buttonLink }; - await client.database.guilds.write(interaction.guild!.id, { - ["moderation." + i.customId]: { - text: buttonText, - link: buttonLink - } - }); - } - } else { - continue; + if (!out) continue + const buttonText = out.fields.getTextInputValue("name"); + const buttonLink = out.fields.getTextInputValue("url").replace(/{id}/gi, "{id}"); + const current = chosen; + if (current.text !== buttonText || current.link !== buttonLink) { + chosen = { text: buttonText, link: buttonLink }; + await client.database.guilds.write(interaction.guild!.id, { + ["moderation." + i.customId]: { + text: buttonText, + link: buttonLink + } + }); } } } diff --git a/src/commands/settings/stats.ts b/src/commands/settings/stats.ts index b840fb0..cdd218b 100644 --- a/src/commands/settings/stats.ts +++ b/src/commands/settings/stats.ts @@ -20,7 +20,8 @@ const command = (builder: SlashCommandSubcommandBuilder) => .setAutocomplete(true) ); -const callback = async (interaction: CommandInteraction): Promise => { +const callback = async (interaction: CommandInteraction): Promise => { // TODO: This command feels unintuitive. Clicking a channel in the select menu deletes it + // instead, it should give a submenu to edit the channel, enable/disable or delete it singleNotify("statsChannelDeleted", interaction.guild!.id, true); const m = (await interaction.reply({ embeds: LoadingEmbed, @@ -30,7 +31,7 @@ const callback = async (interaction: CommandInteraction): Promise => { let config = await client.database.guilds.read(interaction.guild!.id); if (interaction.options.get("name")?.value as string) { let channel; - if (Object.keys(config["stats"]).length >= 25) { + if (Object.keys(config.stats).length >= 25) { return await interaction.editReply({ embeds: [ new EmojiEmbed() @@ -151,7 +152,7 @@ const callback = async (interaction: CommandInteraction): Promise => { let timedOut = false; while (!timedOut) { config = await client.database.guilds.read(interaction.guild!.id); - const stats = config["stats"]; + const stats = config.stats; const selectMenu = new StringSelectMenuBuilder() .setCustomId("remove") .setMinValues(1) diff --git a/src/config/emojis.json b/src/config/emojis.json index 4bdadee..26d4c98 100644 --- a/src/config/emojis.json +++ b/src/config/emojis.json @@ -82,10 +82,6 @@ "EDIT": "951957316071223336", "DELETE": "729064530981683200" }, - "STORE": { - "CREATE": "729064530709315715", - "DELETE": "729064530768035922" - }, "CATEGORY": { "CREATE": "787987508465238026", "EDIT": "787987508565770300", diff --git a/src/events/channelDelete.ts b/src/events/channelDelete.ts index 2c48b02..4780700 100644 --- a/src/events/channelDelete.ts +++ b/src/events/channelDelete.ts @@ -1,5 +1,7 @@ import { + AuditLogEvent, BaseGuildTextChannel, + ChannelType, GuildAuditLogsEntry, GuildBasedChannel, StageChannel, @@ -13,34 +15,47 @@ export const event = "channelDelete"; export async function callback(client: NucleusClient, channel: GuildBasedChannel) { const { getAuditLog, log, NucleusColors, entry, renderDelta, renderUser } = client.logger; - - const auditLog = await getAuditLog(channel.guild, "CHANNEL_DELETE"); - const audit = auditLog.entries.filter((entry: GuildAuditLogsEntry) => entry.target!.id === channel.id).first(); - if (audit.executor.id === client.user.id) return; + // const audit = auditLog.entries.filter((entry: GuildAuditLogsEntry) => entry.target!.id === channel.id).first(); + const auditLog = (await getAuditLog(channel.guild, AuditLogEvent.ChannelDelete)) + .filter((entry: GuildAuditLogsEntry) => (entry.target as GuildBasedChannel)!.id === channel.id)[0]; + if (!auditLog) return; + if (auditLog.executor!.id === client.user!.id) return; let emoji; let readableType; let displayName; switch (channel.type) { - case "GUILD_TEXT": { + case ChannelType.GuildText: { emoji = "CHANNEL.TEXT.DELETE"; readableType = "Text"; displayName = "Text Channel"; break; - } - case "GUILD_VOICE": { + } case ChannelType.GuildAnnouncement: { + emoji = "CHANNEL.TEXT.DELETE"; + readableType = "Announcement"; + displayName = "Announcement Channel"; + break; + } case ChannelType.GuildVoice: { emoji = "CHANNEL.VOICE.DELETE"; readableType = "Voice"; displayName = "Voice Channel"; break; - } - case "GUILD_CATEGORY": { + } case ChannelType.GuildCategory: { emoji = "CHANNEL.CATEGORY.DELETE"; readableType = "Category"; displayName = "Category"; break; - } - default: { + } case ChannelType.GuildStageVoice: { + emoji = "CHANNEL.VOICE.DELETE"; + readableType = "Stage"; + displayName = "Stage Channel"; + break; + } case ChannelType.GuildForum: { + emoji = "CHANNEL.TEXT.DELETE"; + readableType = "Forum"; + displayName = "Forum Channel"; + break; + } default: { emoji = "CHANNEL.TEXT.DELETE"; readableType = "Channel"; displayName = "Channel"; @@ -66,9 +81,9 @@ export async function callback(client: NucleusClient, channel: GuildBasedChannel channel.parent ? channel.parent.name : "Uncategorised" ), nsfw: null, - created: entry(channel.createdTimestamp, renderDelta(channel.createdTimestamp)), + created: entry(channel.createdTimestamp, renderDelta(channel.createdTimestamp!)), deleted: entry(new Date().getTime(), renderDelta(new Date().getTime())), - deletedBy: entry(audit.executor.id, renderUser(audit.executor)) + deletedBy: entry(auditLog.executor!.id, renderUser(auditLog.executor!)) }; if ((channel instanceof BaseGuildTextChannel || channel instanceof StageChannel) && channel.topic !== null) list.topic = entry(channel.topic, `\`\`\`\n${channel.topic.replace("`", "'")}\n\`\`\``); @@ -94,7 +109,7 @@ export async function callback(client: NucleusClient, channel: GuildBasedChannel calculateType: "channelUpdate", color: NucleusColors.red, emoji: emoji, - timestamp: audit.createdTimestamp + timestamp: auditLog.createdTimestamp }, list: list, hidden: { diff --git a/src/events/commandError.ts b/src/events/commandError.ts index ce50122..8ed01d2 100644 --- a/src/events/commandError.ts +++ b/src/events/commandError.ts @@ -1,14 +1,17 @@ +import type { ButtonInteraction, ContextMenuCommandInteraction } from 'discord.js'; +import type { CommandInteraction } from 'discord.js'; +import type { NucleusClient } from "../utils/client.js"; import EmojiEmbed from "../utils/generateEmojiEmbed.js"; export const event = "commandError"; -export async function callback(client, interaction, error) { +export async function callback(_: NucleusClient, interaction: CommandInteraction | ButtonInteraction | ContextMenuCommandInteraction, error: string) { if (interaction.replied || interaction.deferred) { await interaction.followUp({ embeds: [ new EmojiEmbed() .setTitle("Something went wrong") - .setDescription(error.message ?? error.toString()) + .setDescription(error) .setStatus("Danger") .setEmoji("CONTROL.BLOCKCROSS") ], @@ -19,7 +22,7 @@ export async function callback(client, interaction, error) { embeds: [ new EmojiEmbed() .setTitle("Something went wrong") - .setDescription(error.message ?? error.toString()) + .setDescription(error) .setStatus("Danger") .setEmoji("CONTROL.BLOCKCROSS") ], diff --git a/src/utils/log.ts b/src/utils/log.ts index 3f46f86..4565251 100644 --- a/src/utils/log.ts +++ b/src/utils/log.ts @@ -25,7 +25,7 @@ export const Logger = { const delta = num2 - num1; return `${num1} -> ${num2} (${delta > 0 ? "+" : ""}${delta})`; }, - entry(value: string | number | null, displayValue: string): { value: string | null; displayValue: string } { + entry(value: string | number | boolean | null, displayValue: string): { value: string | boolean | null; displayValue: string } { if (typeof value === "number") value = value.toString(); return { value: value, displayValue: displayValue }; }, diff --git a/tsconfig.json b/tsconfig.json index 6bb1cff..a39c584 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,5 +14,5 @@ "noImplicitReturns": false }, "include": ["src/**/*"], - "exclude": [] + "exclude": ["src/Unfinished/**/*"] }