More commands fixed and purge to here

pull/17/head
PineaFan 3 years ago
parent c729e76ee0
commit a34d04b272
No known key found for this signature in database
GPG Key ID: 0AEF25BAA0FB1C74

@ -53,6 +53,7 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
false,
undefined,
"The user will be sent a DM",
null,
"ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
notify
)

@ -1,3 +1,4 @@
import { LoadingEmbed } from './../../utils/defaultEmbeds.js';
import type { HistorySchema } from "../../utils/database.js";
import Discord, {
CommandInteraction,
@ -8,11 +9,12 @@ import Discord, {
ButtonBuilder,
MessageComponentInteraction,
ModalSubmitInteraction,
TextInputComponent,
ButtonStyle,
StringSelectMenuInteraction
StringSelectMenuInteraction,
TextInputStyle,
APIMessageComponentEmoji
} from "discord.js";
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { SlashCommandSubcommandBuilder, StringSelectMenuOptionBuilder } from "@discordjs/builders";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import client from "../../utils/client.js";
@ -153,60 +155,59 @@ async function showHistory(member: Discord.GuildMember, interaction: CommandInte
}
}
if (pageIndex === null) pageIndex = 0;
const components = (
openFilterPane
? [
new ActionRowBuilder<Discord.StringSelectMenuBuilder>().addComponents(
new Discord.SelectMenuBuilder()
.setOptions(
Object.entries(types).map(([key, value]) => ({
label: value.text,
value: key,
default: filteredTypes.includes(key),
emoji: client.emojis.resolve(getEmojiByName(value.emoji, "id"))
}))
)
.setMinValues(1)
.setMaxValues(Object.keys(types).length)
.setCustomId("filter")
.setPlaceholder("Select at least one event")
)
]
: []
).concat([
new ActionRowBuilder<Discord.ButtonBuilder>().addComponents([
new ButtonBuilder()
.setCustomId("prevYear")
.setLabel((currentYear - 1).toString())
.setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
.setStyle(ButtonStyle.Secondary),
new ButtonBuilder().setCustomId("prevPage").setLabel("Previous page").setStyle(ButtonStyle.Primary),
new ButtonBuilder().setCustomId("today").setLabel("Today").setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId("nextPage")
.setLabel("Next page")
.setStyle(ButtonStyle.Primary)
.setDisabled(pageIndex >= groups.length - 1 && currentYear === new Date().getFullYear()),
new ButtonBuilder()
.setCustomId("nextYear")
.setLabel((currentYear + 1).toString())
.setEmoji(getEmojiByName("CONTROL.RIGHT", "id"))
.setStyle(ButtonStyle.Secondary)
.setDisabled(currentYear === new Date().getFullYear())
]),
new ActionRowBuilder<Discord.ButtonBuilder>().addComponents([
new ButtonBuilder()
.setLabel("Mod notes")
.setCustomId("modNotes")
.setStyle(ButtonStyle.Primary)
.setEmoji(getEmojiByName("ICONS.EDIT", "id")),
new ButtonBuilder()
.setLabel("Filter")
.setCustomId("openFilter")
.setStyle(openFilterPane ? ButtonStyle.Success : ButtonStyle.Primary)
.setEmoji(getEmojiByName("ICONS.FILTER", "id"))
])
]);
let components: (ActionRowBuilder<Discord.StringSelectMenuBuilder> | ActionRowBuilder<ButtonBuilder>)[] = []
if (openFilterPane) components = components.concat([
new ActionRowBuilder<Discord.StringSelectMenuBuilder>().addComponents(
new Discord.StringSelectMenuBuilder().setOptions(
...Object.entries(types).map(([key, value]) => new StringSelectMenuOptionBuilder()
.setLabel(value.text)
.setValue(key)
.setDefault(filteredTypes.includes(key))
.setEmoji(client.emojis.resolve(getEmojiByName(value.emoji, "id"))! as APIMessageComponentEmoji)
)
)
.setMinValues(1)
.setMaxValues(Object.keys(types).length)
.setCustomId("filter")
.setPlaceholder("Select events to show")
)
])
components = components.concat([new ActionRowBuilder<Discord.ButtonBuilder>().addComponents([
new ButtonBuilder()
.setCustomId("prevYear")
.setLabel((currentYear - 1).toString())
.setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
.setStyle(ButtonStyle.Secondary),
new ButtonBuilder()
.setCustomId("prevPage")
.setLabel("Previous page")
.setStyle(ButtonStyle.Primary),
new ButtonBuilder().setCustomId("today").setLabel("Today").setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId("nextPage")
.setLabel("Next page")
.setStyle(ButtonStyle.Primary)
.setDisabled(pageIndex >= groups.length - 1 && currentYear === new Date().getFullYear()),
new ButtonBuilder()
.setCustomId("nextYear")
.setLabel((currentYear + 1).toString())
.setEmoji(getEmojiByName("CONTROL.RIGHT", "id"))
.setStyle(ButtonStyle.Secondary)
.setDisabled(currentYear === new Date().getFullYear())
])])
components = components.concat([new ActionRowBuilder<Discord.ButtonBuilder>().addComponents([
new ButtonBuilder()
.setLabel("Mod notes")
.setCustomId("modNotes")
.setStyle(ButtonStyle.Primary)
.setEmoji(getEmojiByName("ICONS.EDIT", "id")),
new ButtonBuilder()
.setLabel("Filter")
.setCustomId("openFilter")
.setStyle(openFilterPane ? ButtonStyle.Success : ButtonStyle.Primary)
.setEmoji(getEmojiByName("ICONS.FILTER", "id"))
])])
const end =
"\n\nJanuary " +
currentYear.toString() +
@ -216,7 +217,7 @@ async function showHistory(member: Discord.GuildMember, interaction: CommandInte
currentYear.toString();
if (groups.length > 0) {
const toRender = groups[Math.min(pageIndex, groups.length - 1)]!;
m = (await interaction.editReply({
m = await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("MEMBER.JOIN")
@ -226,25 +227,25 @@ async function showHistory(member: Discord.GuildMember, interaction: CommandInte
)
.setStatus("Success")
.setFooter({
text: openFilterPane && filteredTypes.length ? "Filters are currently enabled" : ""
text: openFilterPane && filteredTypes.length ? "Filters are currently enabled" : "No filters selected"
})
],
components: components
})) as Message;
});
} else {
m = (await interaction.editReply({
m = await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("MEMBER.JOIN")
.setTitle("Moderation history for " + member.user.username)
.setDescription(`**${currentYear}**\n\n*No events*` + "\n\n" + end)
.setDescription(`**${currentYear}**\n\n*No events*`)
.setStatus("Success")
.setFooter({
text: openFilterPane && filteredTypes.length ? "Filters are currently enabled" : ""
text: openFilterPane && filteredTypes.length ? "Filters are currently enabled" : "No filters selected"
})
],
components: components
})) as Message;
})
}
let i: MessageComponentInteraction;
try {
@ -315,7 +316,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
let m: Message;
const member = interaction.options.getMember("user") as Discord.GuildMember;
await interaction.reply({
embeds: [new EmojiEmbed().setEmoji("NUCLEUS.LOADING").setTitle("Downloading Data").setStatus("Danger")],
embeds: LoadingEmbed,
ephemeral: true,
fetchReply: true
});
@ -324,9 +325,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
let timedOut = false;
while (!timedOut) {
note = await client.database.notes.read(member.guild.id, member.id);
if (firstLoad && !note) {
await showHistory(member, interaction);
}
if (firstLoad && !note) { await showHistory(member, interaction); }
firstLoad = false;
m = (await interaction.editReply({
embeds: [
@ -344,7 +343,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setCustomId("modify")
.setEmoji(getEmojiByName("ICONS.EDIT", "id")),
new ButtonBuilder()
.setLabel("View moderation history")
.setLabel("Moderation history")
.setStyle(ButtonStyle.Primary)
.setCustomId("history")
.setEmoji(getEmojiByName("ICONS.HISTORY", "id"))
@ -364,14 +363,14 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setCustomId("modal")
.setTitle("Editing moderator note")
.addComponents(
new ActionRowBuilder<Discord.TextInputComponent>().addComponents(
new TextInputComponent()
new ActionRowBuilder<Discord.TextInputBuilder>().addComponents(
new Discord.TextInputBuilder()
.setCustomId("note")
.setLabel("Note")
.setMaxLength(4000)
.setRequired(false)
.setStyle("PARAGRAPH")
.setValue(note ? note : "")
.setStyle(TextInputStyle.Paragraph)
.setValue(note ? note : " ")
)
)
);
@ -408,7 +407,9 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (out === null) {
continue;
} else if (out instanceof ModalSubmitInteraction) {
const toAdd = out.fields.getTextInputValue("note") || null;
let toAdd = out.fields.getTextInputValue("note") || null;
if (toAdd === " ") toAdd = null;
if (toAdd) toAdd = toAdd.trim();
await client.database.notes.create(member.guild.id, member.id, toAdd);
} else {
continue;

@ -1,4 +1,4 @@
import { LinkWarningFooter } from './../../utils/defaultEmbeds';
import { LinkWarningFooter } from './../../utils/defaultEmbeds.js';
import { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
// @ts-expect-error
import humanizeDuration from "humanize-duration";
@ -42,6 +42,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
false,
null,
"The user will be sent a DM",
null,
"ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
notify
)

@ -192,6 +192,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
async () =>
await create(interaction.guild, interaction.options.getUser("user")!, interaction.user, reason),
"An appeal ticket will be created when Confirm is clicked",
null,
"CONTROL.TICKET",
createAppealTicket
)
@ -201,6 +202,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
false,
undefined,
null,
null,
"ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
notify
)

@ -45,6 +45,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
false,
null,
null,
null,
"ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
notify
)

@ -1,4 +1,4 @@
import Discord, { CommandInteraction, GuildChannel, GuildMember, TextChannel, ButtonStyle } from "discord.js";
import Discord, { CommandInteraction, GuildChannel, GuildMember, TextChannel, ButtonStyle, ButtonBuilder } from "discord.js";
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import confirmationMessage from "../../utils/confirmationMessage.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
@ -26,7 +26,8 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
);
const callback = async (interaction: CommandInteraction): Promise<unknown> => {
const user = (interaction.options.getMember("user") as GuildMember);
if (!interaction.guild) return;
const user = (interaction.options.getMember("user") as GuildMember | null);
const channel = interaction.channel as GuildChannel;
if (
!["GUILD_TEXT", "GUILD_NEWS", "GUILD_NEWS_THREAD", "GUILD_PUBLIC_THREAD", "GUILD_PRIVATE_THREAD"].includes(
@ -46,7 +47,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
});
}
// TODO:[Modals] Replace this with a modal
if (!interaction.options.getInteger("amount")) {
if (!interaction.options.get("amount")) {
await interaction.reply({
embeds: [
new EmojiEmbed()
@ -74,17 +75,17 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setStatus("Danger")
],
components: [
new Discord.ActionRowBuilder().addComponents([
new Discord.ActionRowBuilder<ButtonBuilder>().addComponents([
new Discord.ButtonBuilder().setCustomId("1").setLabel("1").setStyle(ButtonStyle.Secondary),
new Discord.ButtonBuilder().setCustomId("3").setLabel("3").setStyle(ButtonStyle.Secondary),
new Discord.ButtonBuilder().setCustomId("5").setLabel("5").setStyle(ButtonStyle.Secondary)
]),
new Discord.ActionRowBuilder().addComponents([
new Discord.ActionRowBuilder<ButtonBuilder>().addComponents([
new Discord.ButtonBuilder().setCustomId("10").setLabel("10").setStyle(ButtonStyle.Secondary),
new Discord.ButtonBuilder().setCustomId("25").setLabel("25").setStyle(ButtonStyle.Secondary),
new Discord.ButtonBuilder().setCustomId("50").setLabel("50").setStyle(ButtonStyle.Secondary)
]),
new Discord.ActionRowBuilder().addComponents([
new Discord.ActionRowBuilder<ButtonBuilder>().addComponents([
new Discord.ButtonBuilder()
.setCustomId("done")
.setLabel("Done")
@ -110,16 +111,14 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
}
const amount = parseInt((await component).customId);
let messages;
let messages: Discord.Message[] = [];
await (interaction.channel as TextChannel).messages.fetch({ limit: amount }).then(async (ms) => {
if (user) {
ms = ms.filter((m) => m.author.id === user.id);
}
messages = await (channel as TextChannel).bulkDelete(ms, true);
messages = (await (channel as TextChannel).bulkDelete(ms, true)).map(m => m as Discord.Message);
});
if (messages) {
deleted = deleted.concat(messages.map((m) => m));
}
deleted = deleted.concat(messages);
}
if (deleted.length === 0)
return await interaction.editReply({
@ -136,11 +135,12 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
await client.database.history.create(
"purge",
interaction.guild.id,
user,
interaction.options.getString("reason"),
user.user,
interaction.user,
(interaction.options.get("reason")?.value as (string | null)) ?? "*No reason provided*",
null,
null,
deleted.length
deleted.length.toString()
);
}
const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger;
@ -156,8 +156,8 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
list: {
memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
purgedBy: entry(interaction.user.id, renderUser(interaction.user)),
channel: entry(interaction.channel.id, renderChannel(interaction.channel)),
messagesCleared: entry(deleted.length, deleted.length)
channel: entry(interaction.channel!.id, renderChannel(interaction.channel! as GuildChannel)),
messagesCleared: entry(deleted.length.toString(), deleted.length.toString())
},
hidden: {
guild: interaction.guild.id
@ -189,7 +189,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setStatus("Success")
],
components: [
new Discord.ActionRowBuilder().addComponents([
new Discord.ActionRowBuilder<ButtonBuilder>().addComponents([
new Discord.ButtonBuilder()
.setCustomId("download")
.setLabel("Download transcript")
@ -207,7 +207,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
} catch {
return;
}
if (component && component.customId === "download") {
if (component.customId === "download") {
interaction.editReply({
embeds: [
new EmojiEmbed()
@ -239,12 +239,8 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setDescription(
keyValueList({
channel: `<#${channel.id}>`,
amount: interaction.options.getInteger("amount").toString(),
reason: `\n> ${
interaction.options.getString("reason")
? interaction.options.getString("reason")
: "*No reason provided*"
}`
amount: (interaction.options.get("amount")?.value as number).toString(),
reason: `\n> ${interaction.options.get("reason")?.value ? interaction.options.get("reason")?.value : "*No reason provided*"}`
})
)
.setColor("Danger")
@ -255,7 +251,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
try {
if (!user) {
const toDelete = await (interaction.channel as TextChannel).messages.fetch({
limit: interaction.options.getInteger("amount")
limit: interaction.options.get("amount")?.value as number
});
messages = await (channel as TextChannel).bulkDelete(toDelete, true);
} else {
@ -265,7 +261,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
limit: 100
})
).filter((m) => m.author.id === user.id)
).first(interaction.options.getInteger("amount"));
).first(interaction.options.get("amount")?.value as number);
messages = await (channel as TextChannel).bulkDelete(toDelete, true);
}
} catch (e) {
@ -280,15 +276,29 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
components: []
});
}
if (!messages) {
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("CHANNEL.PURGE.RED")
.setTitle("Purge")
.setDescription("No messages could be deleted")
.setStatus("Danger")
],
components: []
});
return;
}
if (user) {
await client.database.history.create(
"purge",
interaction.guild.id,
user,
interaction.options.getString("reason"),
user.user,
interaction.user,
(interaction.options.get("reason")?.value as (string | null)) ?? "*No reason provided*",
null,
null,
messages.size
messages.size.toString()
);
}
const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger;
@ -304,8 +314,8 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
list: {
memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
purgedBy: entry(interaction.user.id, renderUser(interaction.user)),
channel: entry(interaction.channel.id, renderChannel(interaction.channel)),
messagesCleared: entry(messages.size, messages.size)
channel: entry(interaction.channel!.id, renderChannel(interaction.channel! as GuildChannel)),
messagesCleared: entry(messages.size.toString(), messages.size.toString())
},
hidden: {
guild: interaction.guild.id
@ -314,14 +324,26 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
log(data);
let out = "";
messages.reverse().forEach((message) => {
out += `${message.author.username}#${message.author.discriminator} (${message.author.id}) [${new Date(
message.createdTimestamp
).toISOString()}]\n`;
const lines = message.content.split("\n");
lines.forEach((line) => {
out += `> ${line}\n`;
});
out += "\n\n";
if (!message) {
out += "Unknown message\n\n"
} else {
const author = message.author ?? { username: "Unknown", discriminator: "0000", id: "Unknown" };
out += `${author.username}#${author.discriminator} (${author.id}) [${new Date(
message.createdTimestamp
).toISOString()}]\n`;
if (message.content) {
const lines = message.content.split("\n");
lines.forEach((line) => {
out += `> ${line}\n`;
});
}
if (message.attachments.size > 0) {
message.attachments.forEach((attachment) => {
out += `Attachment > ${attachment.url}\n`;
});
}
out += "\n\n";
}
});
const attachmentObject = {
attachment: Buffer.from(out),
@ -337,7 +359,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setStatus("Success")
],
components: [
new Discord.ActionRowBuilder().addComponents([
new Discord.ActionRowBuilder<ButtonBuilder>().addComponents([
new Discord.ButtonBuilder()
.setCustomId("download")
.setLabel("Download transcript")
@ -355,7 +377,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
} catch {
return;
}
if (component && component.customId === "download") {
if (component.customId === "download") {
interaction.editReply({
embeds: [
new EmojiEmbed()
@ -395,14 +417,15 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
};
const check = (interaction: CommandInteraction) => {
if (!interaction.guild) return false;
const member = interaction.member as GuildMember;
const me = interaction.guild.me!;
const me = interaction.guild.members.me!;
// Check if nucleus has the manage_messages permission
if (!me.permissions.has("MANAGE_MESSAGES")) throw new Error("I do not have the *Manage Messages* permission");
if (!me.permissions.has("ManageMessages")) throw new Error("I do not have the *Manage Messages* permission");
// Allow the owner to purge
if (member.id === interaction.guild.ownerId) return true;
// Check if the user has manage_messages permission
if (!member.permissions.has("MANAGE_MESSAGES")) throw new Error("You do not have the *Manage Messages* permission");
if (!member.permissions.has("ManageMessages")) throw new Error("You do not have the *Manage Messages* permission");
// Allow purge
return true;
};

@ -51,6 +51,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
false,
null,
null,
null,
"ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
notify
)

@ -38,6 +38,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
false,
null,
"The user will be sent a DM",
null,
"ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
notify
)

@ -41,6 +41,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
!(await areTicketsEnabled(interaction.guild.id)),
async () => await create(interaction.guild!, interaction.options.getUser("user")!, interaction.user, reason),
"An appeal ticket will be created",
null,
"CONTROL.TICKET",
createAppealTicket
)
@ -50,6 +51,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
false,
null,
"The user will be sent a DM",
null,
"ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
notify
)

@ -2,6 +2,8 @@ import { AutocompleteInteraction, CommandInteraction, ActionRowBuilder, ButtonBu
import { SlashCommandBuilder } from "@discordjs/builders";
import client from "../utils/client.js";
import EmojiEmbed from "../utils/generateEmojiEmbed.js";
import { capitalize } from "../utils/generateKeyValueList.js";
import { getResults } from "../utils/search.js";
const command = new SlashCommandBuilder()
.setName("tag")
@ -9,41 +11,44 @@ const command = new SlashCommandBuilder()
.addStringOption((o) => o.setName("tag").setDescription("The tag to get").setAutocomplete(true).setRequired(true));
const callback = async (interaction: CommandInteraction): Promise<void> => {
const config = await client.database.guilds.read(interaction.guild.id);
const tags = config.getKey("tags");
const tag = tags[interaction.options.getString("tag")];
const config = await client.database.guilds.read(interaction.guild!.id);
const tags = config.tags;
const search = interaction.options.get("tag")?.value as string;
const tag = tags[search];
if (!tag) {
return await interaction.reply({
await interaction.reply({
embeds: [
new EmojiEmbed()
.setTitle("Tag")
.setDescription(`Tag \`${interaction.options.get("tag")}\` does not exist`)
.setDescription(`Tag \`${search}\` does not exist`)
.setEmoji("PUNISH.NICKNAME.RED")
.setStatus("Danger")
],
ephemeral: true
});
return;
}
let url = "";
let components: ActionRowBuilder[] = [];
let components: ActionRowBuilder<ButtonBuilder>[] = [];
if (tag.match(/^(http|https):\/\/[^ "]+$/)) {
url = tag;
components = [
new ActionRowBuilder().addComponents([new ButtonBuilder().setLabel("Open").setURL(url).setStyle(ButtonStyle.Link)])
new ActionRowBuilder<ButtonBuilder>().addComponents([new ButtonBuilder().setLabel("Open").setURL(url).setStyle(ButtonStyle.Link)])
];
}
return await interaction.reply({
embeds: [
new EmojiEmbed()
.setTitle(interaction.options.get("tag").value)
.setDescription(tag)
.setEmoji("PUNISH.NICKNAME.GREEN")
.setStatus("Success")
.setImage(url)
],
const em = new EmojiEmbed()
.setTitle(capitalize(search))
.setEmoji("PUNISH.NICKNAME.GREEN")
.setStatus("Success")
if (url) em.setImage(url)
else em.setDescription(tag);
await interaction.reply({
embeds: [em],
components: components,
ephemeral: true
});
return;
};
const check = () => {
@ -52,9 +57,12 @@ const check = () => {
const autocomplete = async (interaction: AutocompleteInteraction): Promise<string[]> => {
if (!interaction.guild) return [];
const config = await client.database.guilds.read(interaction.guild.id);
const tags = Object.keys(config.getKey("tags"));
return tags;
const prompt = interaction.options.getString("tag");
console.log(prompt)
const possible = Object.keys((await client.memory.readGuildInfo(interaction.guild.id)).tags);
const results = getResults(prompt ?? "", possible);
console.log(results)
return results;
};
export { command };

@ -16,8 +16,8 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
);
const callback = async (interaction: CommandInteraction): Promise<unknown> => {
const name = interaction.options.getString("name");
const value = interaction.options.getString("value");
const name = interaction.options.get("name")?.value as string;
const value = interaction.options.get("value")?.value as string;
if (name!.length > 100)
return await interaction.reply({
embeds: [
@ -41,7 +41,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
ephemeral: true
});
const data = await client.database.guilds.read(interaction.guild!.id);
if (data.tags.length >= 100)
if (Object.keys(data.tags).length >= 100)
return await interaction.reply({
embeds: [
new EmojiEmbed()
@ -90,6 +90,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
await client.database.guilds.write(interaction.guild!.id, {
[`tags.${name}`]: value
});
await client.memory.forceUpdate(interaction.guild!.id);
} catch (e) {
return await interaction.editReply({
embeds: [
@ -116,7 +117,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
if (!member.permissions.has("MANAGE_MESSAGES"))
if (!member.permissions.has("ManageMessages"))
throw new Error("You must have the *Manage Messages* permission to use this command");
return true;
};

@ -13,7 +13,7 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.addStringOption((o) => o.setName("name").setRequired(true).setDescription("The name of the tag"));
const callback = async (interaction: CommandInteraction): Promise<unknown> => {
const name = interaction.options.getString("name");
const name = interaction.options.get("name")?.value as string;
const data = await client.database.guilds.read(interaction.guild!.id);
if (!data.tags[name])
return await interaction.reply({
@ -51,6 +51,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
});
try {
await client.database.guilds.write(interaction.guild!.id, null, ["tags." + name]);
await client.memory.forceUpdate(interaction.guild!.id);
} catch (e) {
console.log(e);
return await interaction.editReply({
@ -78,7 +79,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
if (!member.permissions.has("MANAGE_MESSAGES"))
if (!member.permissions.has("ManageMessages"))
throw new Error("You must have the *Manage Messages* permission to use this command");
return true;
};

@ -110,6 +110,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
toSet[`tags.${newname}`] = data.tags[name];
}
await client.database.guilds.write(interaction.guild.id, toSet === {} ? null : toSet, toUnset);
await client.memory.forceUpdate(interaction.guild!.id);
} catch (e) {
return await interaction.editReply({
embeds: [
@ -136,7 +137,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
const check = (interaction: CommandInteraction) => {
const member = interaction.member as GuildMember;
if (!member.permissions.has("MANAGE_MESSAGES"))
if (!member.permissions.has("ManageMessages"))
throw new Error("You must have the *Manage Messages* permission to use this command");
return true;
};

@ -1,4 +1,4 @@
import { LoadingEmbed } from "./../../utils/defaultEmbeds.js";
import { LoadingEmbed, Embed } from "./../../utils/defaultEmbeds.js";
import Discord, {
CommandInteraction,
GuildMember,
@ -25,30 +25,6 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
option.setName("user").setDescription("The user to get info about | Default: Yourself")
);
class Embed {
embed: EmojiEmbed = new EmojiEmbed();
title: string = "";
description = "";
pageId = 0;
setEmbed(embed: EmojiEmbed) {
this.embed = embed;
return this;
}
setTitle(title: string) {
this.title = title;
return this;
}
setDescription(description: string) {
this.description = description;
return this;
}
setPageId(pageId: number) {
this.pageId = pageId;
return this;
}
}
const callback = async (interaction: CommandInteraction): Promise<void> => {
const guild = interaction.guild!;
const member = (interaction.options.getMember("user") ?? interaction.member) as Discord.GuildMember;
@ -101,11 +77,11 @@ async function userAbout(guild: Discord.Guild, member: Discord.GuildMember, inte
BugHunterLevel2: "Bug Hunter Level 2",
Partner: "Partnered Server Owner",
Staff: "Discord Staff",
VerifiedDeveloper: "Verified Bot Developer"
// ActiveDeveloper
VerifiedDeveloper: "Verified Bot Developer",
ActiveDeveloper: "Active Developer",
Quarantined: "Quarantined [What does this mean?](https://support.discord.com/hc/en-us/articles/6461420677527)",
Spammer: "Likely Spammer"
// CertifiedModerator
// Quarantined https://discord-api-types.dev/api/discord-api-types-v10/enum/UserFlags#Quarantined
// Spammer https://discord-api-types.dev/api/discord-api-types-v10/enum/UserFlags#Spammer
// VerifiedBot
};
const members = await guild.members.fetch();
@ -121,8 +97,8 @@ async function userAbout(guild: Discord.Guild, member: Discord.GuildMember, inte
let s = "";
let count = 0;
let ended = false;
for (const roleId in roles) {
const string = `<@&${roleId}>, `;
for (const roleId of roles) {
const string = `<@&${roleId[1].id}>, `;
if (s.length + string.length > 1000) {
ended = true;
s += `and ${roles.size - count} more`;

@ -190,7 +190,10 @@
"BugHunterLevel2": "775783766130950234",
"Partner": "775783766178005033",
"Staff": "775783766383788082",
"VerifiedDeveloper": "775783766425600060"
"VerifiedDeveloper": "775783766425600060",
"Quarantined": "1059794708638994474",
"Spammer": "1059794708638994474",
"ActiveDeveloper": "1059795592966053918"
},
"VOICE": {
"CONNECT": "784785219391193138",

@ -0,0 +1,279 @@
import confirmationMessage from '../../utils/confirmationMessage.js';
import EmojiEmbed from '../../utils/generateEmojiEmbed.js';
import { LoadingEmbed } from './../../utils/defaultEmbeds.js';
import Discord, { ActionRowBuilder, ButtonBuilder, ButtonStyle, ContextMenuCommandBuilder, GuildTextBasedChannel, MessageContextMenuCommandInteraction } from "discord.js";
import client from "../../utils/client.js";
import getEmojiByName from '../../utils/getEmojiByName.js';
const command = new ContextMenuCommandBuilder()
.setName("Purge up to here")
async function waitForButton(m: Discord.Message, member: Discord.GuildMember): Promise<boolean> {
let component;
try {
component = m.awaitMessageComponent({ time: 200000, filter: (i) => i.user.id === member.id });
} catch (e) {
return false;
}
(await component).deferUpdate();
return true;
}
const callback = async (interaction: MessageContextMenuCommandInteraction) => {
await interaction.targetMessage.fetch();
const targetMessage = interaction.targetMessage;
const targetMember: Discord.User = targetMessage.author;
let allowedMessage: Discord.Message | undefined = undefined;
const channel = interaction.channel;
if (!channel) return;
await interaction.reply({ embeds: LoadingEmbed, ephemeral: true, fetchReply: true });
// Option for "include this message"?
// Option for "Only selected user"?
const history: Discord.Collection<string, Discord.Message> = await channel.messages.fetch({ limit: 100 });
if (Date.now() - targetMessage.createdTimestamp > 2 * 7 * 24 * 60 * 60 * 1000) {
const m = await interaction.editReply({ embeds: [new EmojiEmbed()
.setTitle("Purge")
.setDescription("The message you selected is older than 2 weeks. Discord only allows bots to delete messages that are 2 weeks old or younger.")
.setEmoji("CHANNEL.PURGE.RED")
.setStatus("Danger")
], components: [
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setCustomId("oldest")
.setLabel("Select first allowed message")
.setStyle(ButtonStyle.Primary),
)
]});
if (!await waitForButton(m, interaction.member as Discord.GuildMember)) return;
} else if (!history.has(targetMessage.id)) {
const m = await interaction.editReply({ embeds: [new EmojiEmbed()
.setTitle("Purge")
.setDescription("The message you selected is not in the last 100 messages in this channel. Discord only allows bots to delete 100 messages at a time.")
.setEmoji("CHANNEL.PURGE.YELLOW")
.setStatus("Warning")
], components: [
new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setCustomId("oldest")
.setLabel("Select first allowed message")
.setStyle(ButtonStyle.Primary),
)
]});
if (!await waitForButton(m, interaction.member as Discord.GuildMember)) return;
} else {
allowedMessage = targetMessage;
}
if (!allowedMessage) {
// Find the oldest message thats younger than 2 weeks
const messages = history.filter(m => Date.now() - m.createdTimestamp < 2 * 7 * 24 * 60 * 60 * 1000);
allowedMessage = messages.sort((a, b) => a.createdTimestamp - b.createdTimestamp).first();
}
if (!allowedMessage) {
await interaction.editReply({ embeds: [new EmojiEmbed()
.setTitle("Purge")
.setDescription("There are no valid messages in the last 100 messages. (No messages younger than 2 weeks)")
.setEmoji("CHANNEL.PURGE.RED")
.setStatus("Danger")
], components: [] });
return;
}
let reason: string | null = null
let confirmation;
let chosen = false;
let timedOut = false;
let deleteSelected = true;
let deleteUser = false;
do {
confirmation = await new confirmationMessage(interaction)
.setEmoji("CHANNEL.PURGE.RED")
.setTitle("Purge")
.setDescription(
`[[Selected Message]](${allowedMessage.url})\n\n` +
(reason ? "\n> " + reason.replaceAll("\n", "\n> ") : "*No reason provided*") + "\n\n" +
`Are you sure you want to delete all messages from below the selected message?`
)
.addCustomBoolean(
"includeSelected",
"Include selected message",
false,
undefined,
"The selected message will be deleted as well.",
"The selected message will not be deleted.",
"CONTROL." + (deleteSelected ? "TICK" : "CROSS"),
deleteSelected
)
.addCustomBoolean(
"onlySelectedUser",
"Only selected user",
false,
undefined,
`Only messages from <@${targetMember.id}> will be deleted.`,
`All messages will be deleted.`,
"CONTROL." + (deleteUser ? "TICK" : "CROSS"),
deleteUser
)
.setColor("Danger")
.addReasonButton(reason ?? "")
.send(true)
reason = reason ?? ""
if (confirmation.cancelled) timedOut = true;
else if (confirmation.success !== undefined) chosen = true;
else if (confirmation.newReason) reason = confirmation.newReason;
else if (confirmation.components) {
deleteSelected = confirmation.components["includeSelected"]!.active;
deleteUser = confirmation.components["onlySelectedUser"]!.active;
}
} while (!chosen && !timedOut);
if (timedOut) return;
if (!confirmation.success) {
await interaction.editReply({ embeds: [new EmojiEmbed()
.setTitle("Purge")
.setDescription("No changes were made")
.setEmoji("CHANNEL.PURGE.GREEN")
.setStatus("Success")
], components: [] });
return;
}
const filteredMessages = history
.filter(m => m.createdTimestamp >= allowedMessage!.createdTimestamp) // older than selected
.filter(m => deleteUser ? m.author.id === targetMember.id : true) // only selected user
.filter(m => deleteSelected ? true : m.id !== allowedMessage!.id) // include selected
const deleted = await (channel as GuildTextBasedChannel).bulkDelete(filteredMessages, true);
if (deleted.size === 0) {
return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("CHANNEL.PURGE.RED")
.setTitle("Purge")
.setDescription("No messages were deleted")
.setStatus("Danger")
],
components: []
});
}
if (deleteUser) {
await client.database.history.create(
"purge",
interaction.guild!.id,
targetMember,
interaction.user,
reason === "" ? "*No reason provided*" : reason,
null,
null,
deleted.size.toString()
);
}
const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger;
const data = {
meta: {
type: "channelPurge",
displayName: "Channel Purged",
calculateType: "messageDelete",
color: NucleusColors.red,
emoji: "PUNISH.BAN.RED",
timestamp: new Date().getTime()
},
list: {
memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
purgedBy: entry(interaction.user.id, renderUser(interaction.user)),
channel: entry(interaction.channel!.id, renderChannel(interaction.channel! as Discord.GuildChannel)),
messagesCleared: entry(deleted.size.toString(), deleted.size.toString())
},
hidden: {
guild: interaction.guild!.id
}
};
log(data);
let out = "";
deleted.reverse().forEach((message) => {
if (!message) {
out += "Unknown message\n\n"
} else {
const author = message.author ?? { username: "Unknown", discriminator: "0000", id: "Unknown" };
out += `${author.username}#${author.discriminator} (${author.id}) [${new Date(
message.createdTimestamp
).toISOString()}]\n`;
if (message.content) {
const lines = message.content.split("\n");
lines.forEach((line) => {
out += `> ${line}\n`;
});
}
if (message.attachments.size > 0) {
message.attachments.forEach((attachment) => {
out += `Attachment > ${attachment.url}\n`;
});
}
out += "\n\n";
}
});
const attachmentObject = {
attachment: Buffer.from(out),
name: `purge-${channel.id}-${Date.now()}.txt`,
description: "Purge log"
};
const m = (await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("CHANNEL.PURGE.GREEN")
.setTitle("Purge")
.setDescription("Messages cleared")
.setStatus("Success")
],
components: [
new Discord.ActionRowBuilder<ButtonBuilder>().addComponents([
new Discord.ButtonBuilder()
.setCustomId("download")
.setLabel("Download transcript")
.setStyle(ButtonStyle.Success)
.setEmoji(getEmojiByName("CONTROL.DOWNLOAD", "id"))
])
]
})) as Discord.Message;
let component;
try {
component = await m.awaitMessageComponent({
filter: (m) => m.user.id === interaction.user.id,
time: 300000
});
} catch {
return;
}
if (component.customId === "download") {
interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("CHANNEL.PURGE.GREEN")
.setTitle("Purge")
.setDescription("Transcript uploaded above")
.setStatus("Success")
],
components: [],
files: [attachmentObject]
});
} else {
interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("CHANNEL.PURGE.GREEN")
.setTitle("Purge")
.setDescription("Messages cleared")
.setStatus("Success")
],
components: []
});
}
}
const check = async (_interaction: MessageContextMenuCommandInteraction) => {
return true;
}
export { command, callback, check }

@ -0,0 +1,18 @@
import { ContextMenuCommandBuilder, GuildMember, UserContextMenuCommandInteraction } from "discord.js";
import { userAbout } from "../../commands/user/about.js";
const command = new ContextMenuCommandBuilder()
.setName("User info")
const callback = async (interaction: UserContextMenuCommandInteraction) => {
const guild = interaction.guild!
let member = interaction.targetMember
if (!member) member = await guild.members.fetch(interaction.targetId)
await userAbout(guild, member as GuildMember, interaction)
}
const check = async (_interaction: UserContextMenuCommandInteraction) => {
return true;
}
export { command, callback, check }

@ -3,71 +3,15 @@ import verify from "../reflex/verify.js";
import create from "../actions/tickets/create.js";
import close from "../actions/tickets/delete.js";
import createTranscript from "../premium/createTranscript.js";
import Fuse from "fuse.js";
import { autocomplete as tagAutocomplete } from "../commands/tag.js";
import type { AutocompleteInteraction, Interaction, MessageComponentInteraction } from "discord.js";
import type { Interaction, MessageComponentInteraction } from "discord.js";
import type { NucleusClient } from "../utils/client.js";
export const event = "interactionCreate";
function getAutocomplete(typed: string, options: string[]): { name: string; value: string }[] {
options = options.filter((option) => option.length <= 100); // thanks discord. 6000 character limit on slash command inputs but only 100 for autocomplete.
if (!typed)
return options
.slice(0, 25)
.sort()
.map((option) => ({ name: option, value: option }));
const fuse = new Fuse(options, {
useExtendedSearch: true,
findAllMatches: true,
minMatchCharLength: 0
}).search(typed);
return fuse.slice(0, 25).map((option) => ({ name: option.item, value: option.item }));
}
function generateStatsChannelAutocomplete(typed: string) {
const validReplacements = ["serverName", "memberCount", "memberCount:bots", "memberCount:humans"];
const autocompletions = [];
const beforeLastOpenBracket = typed.match(/(.*){[^{}]{0,15}$/);
if (beforeLastOpenBracket !== null) {
for (const replacement of validReplacements) {
autocompletions.push(`${beforeLastOpenBracket[1]} {${replacement}}`);
}
} else {
for (const replacement of validReplacements) {
autocompletions.push(`${typed} {${replacement}}`);
}
}
return getAutocomplete(typed, autocompletions);
}
function generateWelcomeMessageAutocomplete(typed: string) {
const validReplacements = [
"serverName",
"memberCount",
"memberCount:bots",
"memberCount:humans",
"member:mention",
"member:name"
];
const autocompletions = [];
const beforeLastOpenBracket = typed.match(/(.*){[^{}]{0,15}$/);
if (beforeLastOpenBracket !== null) {
for (const replacement of validReplacements) {
autocompletions.push(`${beforeLastOpenBracket[1]} {${replacement}}`);
}
} else {
for (const replacement of validReplacements) {
autocompletions.push(`${typed} {${replacement}}`);
}
}
return getAutocomplete(typed, autocompletions);
}
async function interactionCreate(interaction: Interaction) {
if (
interaction.type === "MESSAGE_COMPONENT" &&
(interaction as MessageComponentInteraction).componentType === "BUTTON"
) {
if (interaction.isButton()) {
const int = interaction as MessageComponentInteraction;
switch (int.customId) {
case "rolemenu": {
@ -86,19 +30,19 @@ async function interactionCreate(interaction: Interaction) {
return createTranscript(int);
}
}
} else if (interaction.type === "APPLICATION_COMMAND_AUTOCOMPLETE") {
const int = interaction as AutocompleteInteraction;
switch (`${int.commandName} ${int.options.getSubcommandGroup(false)} ${int.options.getSubcommand(false)}`) {
case "tag null null": {
return int.respond(getAutocomplete(int.options.getString("tag") ?? "", await tagAutocomplete(int)));
}
case "settings null stats": {
return int.respond(generateStatsChannelAutocomplete(int.options.getString("name") ?? ""));
}
case "settings null welcome": {
return int.respond(generateWelcomeMessageAutocomplete(int.options.getString("message") ?? ""));
}
}
// } else if (interaction.type === "APPLICATION_COMMAND_AUTOCOMPLETE") {
// const int = interaction as AutocompleteInteraction;
// switch (`${int.commandName} ${int.options.getSubcommandGroup(false)} ${int.options.getSubcommand(false)}`) {
// case "tag null null": {
// return int.respond(getAutocomplete(int.options.getString("tag") ?? "", await tagAutocomplete(int)));
// }
// case "settings null stats": {
// return int.respond(generateStatsChannelAutocomplete(int.options.getString("name") ?? ""));
// }
// case "settings null welcome": {
// return int.respond(generateWelcomeMessageAutocomplete(int.options.getString("message") ?? ""));
// }
// }
}
}

@ -12,10 +12,9 @@ interface PropSchema {
export async function callback(client: NucleusClient, member?: GuildMember, guild?: Guild, user?: User) {
if (!member && !guild) return;
guild = await client.guilds.fetch(member ? member.guild.id : guild!.id);
if (!guild) return;
user = user ?? member!.user;
const config = await client.database.guilds.read(guild.id);
Object.entries(config.getKey("stats")).forEach(async ([channel, props]) => {
Object.entries(config.stats).forEach(async ([channel, props]) => {
if ((props as PropSchema).enabled) {
let string = (props as PropSchema).name;
if (!string) return;
@ -27,13 +26,13 @@ export async function callback(client: NucleusClient, member?: GuildMember, guil
fetchedChannel = null;
}
if (!fetchedChannel) {
const deleted = config.getKey("stats")[channel];
const deleted = config.stats[channel];
await client.database.guilds.write(guild!.id, null, `stats.${channel}`);
return singleNotify(
"statsChannelDeleted",
guild!.id,
"One or more of your stats channels have been deleted. Please use `/settings stats` if you wish to add the channel again.\n" +
`The channels name was: ${deleted.name}`,
`The channels name was: ${deleted!.name}`,
"Critical"
);
}

@ -1,11 +1,10 @@
import Discord, { Client, Interaction } from 'discord.js';
import Discord, { Client, Interaction, AutocompleteInteraction } from 'discord.js';
import { Logger } from "../utils/log.js";
import Memory from "../utils/memory.js";
import type { VerifySchema } from "../reflex/verify.js";
import { Guilds, History, ModNotes, Premium } from "../utils/database.js";
import EventScheduler from "../utils/eventScheduler.js";
import type { RoleMenuSchema } from "../actions/roleMenu.js";
// @ts-expect-error
import config from "../config/main.json" assert { type: "json" };
@ -28,9 +27,9 @@ class NucleusClient extends Client {
((builder: Discord.SlashCommandBuilder) => Discord.SlashCommandBuilder) |
Discord.SlashCommandSubcommandBuilder | ((builder: Discord.SlashCommandSubcommandBuilder) => Discord.SlashCommandSubcommandBuilder) | Discord.SlashCommandSubcommandGroupBuilder | ((builder: Discord.SlashCommandSubcommandGroupBuilder) => Discord.SlashCommandSubcommandGroupBuilder),
callback: (interaction: Interaction) => Promise<void>,
check: (interaction: Interaction) => Promise<boolean> | boolean
check: (interaction: Interaction) => Promise<boolean> | boolean,
autocomplete: (interaction: AutocompleteInteraction) => Promise<string[]>
}> = {};
// commands: Discord.Collection<string, [Function, Function]> = new Discord.Collection();
constructor(database: typeof NucleusClient.prototype.database) {
super({ intents: 32767 });

@ -1,5 +1,4 @@
import Discord, { Interaction, SlashCommandBuilder, ApplicationCommandType } from 'discord.js';
// @ts-expect-error
import config from "../../config/main.json" assert { type: "json" };
import client from "../client.js";
import fs from "fs";
@ -140,19 +139,31 @@ async function registerCommandHandler() {
const commandName = "contextCommands/message/" + interaction.commandName;
execute(client.commands[commandName]?.check, client.commands[commandName]?.callback, interaction)
return;
} else if (interaction.isAutocomplete()) {
const commandName = interaction.commandName;
const subcommandGroupName = interaction.options.getSubcommandGroup(false);
const subcommandName = interaction.options.getSubcommand(false);
const fullCommandName = "commands/" + commandName + (subcommandGroupName ? `/${subcommandGroupName}` : "") + (subcommandName ? `/${subcommandName}` : "");
const choices = await client.commands[fullCommandName]?.autocomplete(interaction);
const formatted = (choices ?? []).map(choice => {
return { name: choice, value: choice }
})
interaction.respond(formatted)
} else if (interaction.isChatInputCommand()) {
const commandName = interaction.commandName;
const subcommandGroupName = interaction.options.getSubcommandGroup(false);
const subcommandName = interaction.options.getSubcommand(false);
const fullCommandName = "commands/" + commandName + (subcommandGroupName ? `/${subcommandGroupName}` : "") + (subcommandName ? `/${subcommandName}` : "");
const command = client.commands[fullCommandName];
const callback = command?.callback;
const check = command?.check;
execute(check, callback, interaction);
}
if (!interaction.isChatInputCommand()) return;
const commandName = interaction.commandName;
const subcommandGroupName = interaction.options.getSubcommandGroup(false);
const subcommandName = interaction.options.getSubcommand(false);
const fullCommandName = "commands/" + commandName + (subcommandGroupName ? `/${subcommandGroupName}` : "") + (subcommandName ? `/${subcommandName}` : "");
const command = client.commands[fullCommandName];
const callback = command?.callback;
const check = command?.check;
execute(check, callback, interaction);
});
}

@ -1,6 +1,5 @@
import type { SlashCommandSubcommandGroupBuilder } from "@discordjs/builders";
import type { SlashCommandBuilder } from "discord.js";
// @ts-expect-error
import config from "../../config/main.json" assert { type: "json" };
import getSubcommandsInFolder from "./getFilesInFolder.js";
import client from "../client.js";

@ -18,6 +18,7 @@ interface CustomBoolean<T> {
title: string;
disabled: boolean;
value: string | null;
notValue: string | null;
emoji: string | undefined;
active: boolean;
onClick: () => Promise<T>;
@ -68,6 +69,7 @@ class confirmationMessage {
disabled: boolean,
callback: (() => Promise<unknown>) | null = async () => null,
callbackClicked: string | null,
callbackNotClicked: string | null,
emoji?: string,
initial?: boolean
) {
@ -75,6 +77,7 @@ class confirmationMessage {
title: title,
disabled: disabled,
value: callbackClicked,
notValue: callbackNotClicked,
emoji: emoji,
active: initial ?? false,
onClick: callback ?? (async () => null),
@ -145,10 +148,12 @@ class confirmationMessage {
"\n\n" +
Object.values(this.customButtons)
.map((v) => {
if (v.value === null) return "";
return v.active ? `*${v.value}*\n` : "";
})
.join("")
if (v.active) {
return v.value ? `*${v.value}*\n` : "";
} else {
return v.notValue ? `*${v.notValue}*\n` : "";
}
}).join("")
)
.setStatus(this.color)
],
@ -163,7 +168,8 @@ class confirmationMessage {
} else {
m = (await this.interaction.reply(object)) as unknown as Message;
}
} catch {
} catch (e) {
console.log(e);
cancelled = true;
continue;
}

@ -1,6 +1,5 @@
import type Discord from "discord.js";
import { Collection, MongoClient } from "mongodb";
// @ts-expect-error
import config from "../config/main.json" assert { type: "json" };
const mongoClient = new MongoClient(config.mongoUrl);
@ -17,7 +16,6 @@ export class Guilds {
}
async setup(): Promise<Guilds> {
// @ts-expect-error
this.defaultData = (await import("../config/default.json", { assert: { type: "json" } }))
.default as unknown as GuildConfig;
return this;

@ -8,4 +8,30 @@ export const LoadingEmbed = [
export const LinkWarningFooter = {
text: "The button below will take you to a website set by the server moderators. Do not enter any passwords unless it is from a trusted website.",
iconURL: "https://cdn.discordapp.com/emojis/952295894370369587.webp?size=128&quality=lossless"
}
}
class Embed {
embed: EmojiEmbed = new EmojiEmbed();
title: string = "";
description = "";
pageId = 0;
setEmbed(embed: EmojiEmbed) {
this.embed = embed;
return this;
}
setTitle(title: string) {
this.title = title;
return this;
}
setDescription(description: string) {
this.description = description;
return this;
}
setPageId(pageId: number) {
this.pageId = pageId;
return this;
}
}
export { Embed };

@ -2,7 +2,6 @@ import { Agenda } from "@hokify/agenda";
import client from "./client.js";
import * as fs from "fs";
import * as path from "path";
// @ts-expect-error
import config from "../config/main.json" assert { type: "json" };
class EventScheduler {

@ -1,4 +1,3 @@
// @ts-expect-error
import emojis from "../config/emojis.json" assert { type: "json" };
interface EmojisIndex {

@ -6,6 +6,7 @@ interface GuildData {
filters: GuildConfig["filters"];
logging: GuildConfig["logging"];
tickets: GuildConfig["tickets"];
tags: GuildConfig["tags"];
}
class Memory {
@ -20,7 +21,7 @@ class Memory {
}
}
}, 1000 * 60 * 30);
}
};
async readGuildInfo(guild: string): Promise<GuildData> {
if (!this.memory.has(guild)) {
@ -29,10 +30,15 @@ class Memory {
lastUpdated: Date.now(),
filters: guildData.filters,
logging: guildData.logging,
tickets: guildData.tickets
tickets: guildData.tickets,
tags: guildData.tags
});
}
return this.memory.get(guild)!;
};
async forceUpdate(guild: string) {
if (this.memory.has(guild)) this.memory.delete(guild);
}
}

@ -0,0 +1,18 @@
import Fuse from "fuse.js";
function getResults(typed: string, options: string[]): string[] {
options = options.filter((option) => option.length <= 100); // thanks discord. 6000 character limit on slash command inputs but only 100 for autocomplete.
if (!typed)
return options
.slice(0, 25)
.sort()
// @ts-expect-error
const fuse = new Fuse(options, {
useExtendedSearch: true,
findAllMatches: true,
minMatchCharLength: typed.length > 3 ? 3 : typed.length,
}).search(typed);
return fuse.slice(0, 25).map((option: {item: string }) => option.item );
}
export { getResults }
Loading…
Cancel
Save