fixed up a few commands
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 @@
`**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 @@
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 @@
.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 @@
.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 @@
.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 @@
.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 @@
.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 @@
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 @@
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 @@
])
]
});
- 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 @@
.setAutocomplete(true)
);
-const callback = async (interaction: CommandInteraction): Promise<unknown> => {
+const callback = async (interaction: CommandInteraction): Promise<unknown> => { // 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 @@
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 @@
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 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 @@
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 @@
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 @@
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 @@
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/**/*"]
}