Tickets! and a lot of bug fixes
diff --git a/src/Unfinished/all.ts b/src/Unfinished/all.ts
index 4fd1202..9d9e653 100644
--- a/src/Unfinished/all.ts
+++ b/src/Unfinished/all.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../utils/defaults.js";
import Discord, {
CommandInteraction,
GuildMember,
diff --git a/src/Unfinished/categorisationTest.ts b/src/Unfinished/categorisationTest.ts
index f5660fe..dc38dfe 100644
--- a/src/Unfinished/categorisationTest.ts
+++ b/src/Unfinished/categorisationTest.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../utils/defaults.js";
import { CommandInteraction, GuildChannel, ActionRowBuilder, ButtonBuilder, SelectMenuBuilder, ButtonStyle } from "discord.js";
import { SlashCommandBuilder } from "@discordjs/builders";
import EmojiEmbed from "../utils/generateEmojiEmbed.js";
@@ -84,7 +84,10 @@
});
let i;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
return await interaction.editReply({
embeds: [
@@ -118,7 +121,7 @@
console.log(categorised);
};
-const check = (_interaction: CommandInteraction) => {
+const check = () => {
return true;
};
diff --git a/src/actions/roleMenu.ts b/src/actions/roleMenu.ts
index 1c493bb..3a01c46 100644
--- a/src/actions/roleMenu.ts
+++ b/src/actions/roleMenu.ts
@@ -1,10 +1,24 @@
-import { Interaction, ButtonBuilder, CommandInteraction, Role, ButtonStyle, ButtonInteraction } from "discord.js";
+import { unknownServerIcon } from './../utils/defaults.js';
+import {
+ Interaction,
+ ButtonBuilder,
+ CommandInteraction,
+ ButtonStyle,
+ ButtonInteraction,
+ StringSelectMenuOptionBuilder,
+ StringSelectMenuBuilder,
+ GuildMemberRoleManager,
+ Role
+} from "discord.js";
import EmojiEmbed from "../utils/generateEmojiEmbed.js";
-import { ActionRowBuilder, SelectMenuBuilder } from "discord.js";
+import { ActionRowBuilder } from "discord.js";
import getEmojiByName from "../utils/getEmojiByName.js";
import client from "../utils/client.js";
-import { LoadingEmbed } from "../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../utils/defaults.js";
import type { GuildConfig } from "../utils/database.js";
+import { roleException } from '../utils/createTemporaryStorage.js';
+import addPlural from '../utils/plurals.js';
+import createPageIndicator from '../utils/createPageIndicator.js';
export interface RoleMenuSchema {
guild: string;
@@ -17,19 +31,17 @@
}
export async function callback(interaction: CommandInteraction | ButtonInteraction) {
- if(!interaction.guild) return interaction.reply({ content: "This command can only be used in a server.", ephemeral: true });
- if(!interaction.member) return interaction.reply({ content: "You must be in a server to use this command.", ephemeral: true });
+ if (!interaction.member) return;
+ if (!interaction.guild) return;
const config = await client.database.guilds.read(interaction.guild.id);
if (!config.roleMenu.enabled)
return await interaction.reply({
embeds: [
new EmojiEmbed()
.setTitle("Roles")
- .setDescription(
- "Self roles are currently disabled. Please contact a staff member or try again later."
- )
+ .setDescription("Self roles are currently disabled. Please contact a staff member or try again later.")
.setStatus("Danger")
- .setEmoji("CONTROL.BLOCKCROSS")
+ .setEmoji("GUILD.GREEN")
],
ephemeral: true
});
@@ -38,167 +50,187 @@
embeds: [
new EmojiEmbed()
.setTitle("Roles")
- .setDescription("There are no roles available. Please contact a staff member or try again later.")
+ .setDescription("There are no roles available. Please contact a staff member if you believe this is a mistake.")
.setStatus("Danger")
- .setEmoji("CONTROL.BLOCKCROSS")
+ .setEmoji("GUILD.GREEN")
],
ephemeral: true
});
- await interaction.reply({ embeds: LoadingEmbed, ephemeral: true });
- let m;
- if (config.roleMenu.allowWebUI) {
- let code = "";
- let length = 5;
- let itt = 0;
- const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
- let valid = false;
- while (!valid) {
- itt += 1;
- code = "";
- for (let i = 0; i < length; i++) {
- code += chars.charAt(Math.floor(Math.random() * chars.length));
- }
- if (code in client.roleMenu) continue;
- if (itt > 1000) {
- itt = 0;
- length += 1;
- continue;
- }
- valid = true;
+ const m = await interaction.reply({ embeds: LoadingEmbed, ephemeral: true });
+ if (config.roleMenu.allowWebUI) { // TODO: Make rolemenu web ui
+ const loginMethods: {webUI: boolean} = {
+ webUI: false
}
- client.roleMenu[code] = {
- guild: interaction.guild.id,
- guildName: interaction.guild.name,
- guildIcon: interaction.guild.iconURL({ extension: "png" }),
- user: interaction.member.user.id,
- username: interaction.member.user.username,
- data: config.roleMenu.options,
- interaction: interaction
- };
- let up = true;
- up = false; // FIXME: Role menu does not yet exist, so the web UI is never up
- /* try {
+ try {
const status = await fetch(client.config.baseUrl).then((res) => res.status);
- if (status !== 200) up = false;
- } catch {
- up = false;
- }*/
- m = await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setTitle("Roles")
- .setDescription("Select how to choose your roles")
- .setStatus("Success")
- .setEmoji("GUILD.GREEN")
- ],
- components: [
- new ActionRowBuilder().addComponents([
- new ButtonBuilder()
- .setLabel("Online")
- .setStyle(ButtonStyle.Link)
- .setDisabled(!up)
- .setURL(`${client.config.baseUrl}nucleus/rolemenu?code=${code}`),
- new ButtonBuilder().setLabel("Manual").setStyle(ButtonStyle.Primary).setCustomId("manual")
- ])
- ]
- });
- }
- let component;
- if (!m) return;
- try {
- component = await m.awaitMessageComponent({ time: 300000 });
- } catch (e) {
- return;
- }
- component.deferUpdate();
- let rolesToAdd: Role[] = [];
- for (let i = 0; i < config.roleMenu.options.length; i++) {
- const object = config.roleMenu.options[i];
- const m = await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setTitle("Roles")
- .setEmoji("GUILD.GREEN")
- .setDescription(
- `**${object.name}**` +
- (object.description ? `\n${object.description}` : "") +
- `\n\nSelect ${object.min}` +
- (object.min !== object.max ? ` to ${object.max}` : "") +
- ` role${object.max === 1 ? "" : "s"} to add.`
- )
- .setStatus("Success")
- .setFooter({
- text: `Step ${i + 1}/${config.roleMenu.options.length}`
- })
- ],
- components: [
- new ActionRowBuilder().addComponents(
- [
+ if (status !== 200) loginMethods.webUI = false;
+ } catch(e) {
+ loginMethods.webUI = false;
+ }
+ if (Object.values(loginMethods).some((i) => i)) {
+ let code = "";
+ if (loginMethods.webUI) {
+ let length = 5;
+ let itt = 0;
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ let valid = false;
+ while (!valid) {
+ itt += 1;
+ code = "";
+ for (let i = 0; i < length; i++) {
+ code += chars.charAt(Math.floor(Math.random() * chars.length));
+ }
+ if (code in client.roleMenu) continue;
+ if (itt > 1000) {
+ itt = 0;
+ length += 1;
+ continue;
+ }
+ valid = true;
+ }
+ client.roleMenu[code] = {
+ guild: interaction.guild.id,
+ guildName: interaction.guild.name,
+ guildIcon: interaction.guild.iconURL({ extension: "png" }) ?? unknownServerIcon,
+ user: interaction.member!.user.id,
+ username: interaction.member!.user.username,
+ data: config.roleMenu.options,
+ interaction: interaction as Interaction
+ };
+ }
+ await interaction.editReply({
+ embeds: [
+ new EmojiEmbed()
+ .setTitle("Roles")
+ .setDescription("Select how to choose your roles")
+ .setStatus("Success")
+ .setEmoji("GUILD.GREEN")
+ ],
+ components: [
+ new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
- .setLabel("Cancel")
- .setStyle(ButtonStyle.Danger)
- .setCustomId("cancel")
- .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
- ].concat(
- object.min === 0
- ? [
- new ButtonBuilder()
- .setLabel("Skip")
- .setStyle(ButtonStyle.Secondary)
- .setCustomId("skip")
- .setEmoji(getEmojiByName("CONTROL.RIGHT", "id"))
- ]
- : []
- )
- )
- ].concat([
- new ActionRowBuilder().addComponents([
- new SelectMenuBuilder()
- .setPlaceholder(`${object.name}`)
- .setCustomId("rolemenu")
- .setMinValues(object.min)
- .setMaxValues(object.max)
- .setOptions(
- object.options.map((o) => {
- return {
- label: o.name,
- description: o.description,
- value: o.role
- };
- })
- )
- ])
- ])
+ .setLabel("Online")
+ .setStyle(ButtonStyle.Link)
+ .setDisabled(!loginMethods.webUI)
+ .setURL(`${client.config.baseUrl}nucleus/rolemenu?code=${code}`),
+ new ButtonBuilder()
+ .setLabel("In Discord")
+ .setStyle(ButtonStyle.Primary)
+ .setCustomId("discord")
+ ])
+ ]
+ });
+ let component;
+ try {
+ component = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
+ } catch (e) {
+ return;
+ }
+ component.deferUpdate();
+ }
+ }
+
+ const options = config.roleMenu.options;
+ const selectedRoles: string[][] = [];
+ const maxPage = options.length;
+ const completedPages: boolean[] = options.map((option) => option.min === 0);
+ for (let i = 0; i < maxPage; i++) { selectedRoles.push([]); }
+
+ let page = 0;
+ let complete = completedPages.every((page) => page);
+ let done = false;
+
+ while (!(complete && done)) {
+ const currentPageData = options[page]!
+ const embed = new EmojiEmbed()
+ .setTitle("Roles")
+ .setDescription(
+ `**${currentPageData.name}**\n` +
+ `> ${currentPageData.description}\n\n` +
+ (currentPageData.min === currentPageData.max ? `Select ${addPlural(currentPageData.min, "role")}` :
+ `Select between ${currentPageData.min} and ${currentPageData.max} roles` + (
+ currentPageData.min === 0 ? ` or press next` : "")) + "\n\n" +
+ createPageIndicator(maxPage, page)
+ )
+ .setStatus("Success")
+ .setEmoji("GUILD.GREEN");
+ const components = [
+ new ActionRowBuilder<ButtonBuilder>().addComponents(
+ new ButtonBuilder()
+ .setStyle(ButtonStyle.Secondary)
+ .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
+ .setCustomId("back")
+ .setDisabled(page === 0),
+ new ButtonBuilder()
+ .setStyle(ButtonStyle.Secondary)
+ .setEmoji(getEmojiByName("CONTROL.RIGHT", "id"))
+ .setCustomId("next")
+ .setDisabled(page === maxPage - 1),
+ new ButtonBuilder()
+ .setStyle(ButtonStyle.Success)
+ .setEmoji(getEmojiByName("CONTROL.TICK", "id"))
+ .setCustomId("done")
+ .setDisabled(!complete)
+ ),
+ new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
+ new StringSelectMenuBuilder()
+ .setCustomId("roles")
+ .setPlaceholder("Select...")
+ .setMinValues(currentPageData.min)
+ .setMaxValues(currentPageData.max)
+ .addOptions(currentPageData.options.map((option) => {
+ const builder = new StringSelectMenuOptionBuilder()
+ .setLabel(option.name)
+ .setValue(option.role)
+ .setDefault(selectedRoles[page]!.includes(option.role));
+ if (option.description) builder.setDescription(option.description);
+ return builder;
+ }))
+ )
+ ];
+ await interaction.editReply({
+ embeds: [embed],
+ components: components
});
let component;
try {
- component = await m.awaitMessageComponent({ time: 300000 });
+ component = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
return;
}
component.deferUpdate();
- if (component.customId === "rolemenu") {
- rolesToAdd = rolesToAdd.concat(component.values);
- } else if (component.customId === "cancel") {
- return await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setTitle("Roles")
- .setDescription("Cancelled. No changes were made.")
- .setStatus("Danger")
- .setEmoji("GUILD.RED")
- ],
- components: []
- });
+ if (component.customId === "back") {
+ page = Math.max(0, page - 1);
+ } else if (component.customId === "next") {
+ page = Math.min(maxPage - 1, page + 1);
+ } else if (component.customId === "done") {
+ done = true;
+ } else if (component.customId === "roles" && component.isStringSelectMenu()) {
+ selectedRoles[page] = component.values;
+ completedPages[page] = true
+ page = Math.min(maxPage - 1, page + 1);
}
+ complete = completedPages.every((page) => page);
}
- let rolesToRemove = config.roleMenu.options.map((o) => o.options.map((o) => o.role)).flat();
- const memberRoles = interaction.member.roles.cache.map((r) => r.id);
- rolesToRemove = rolesToRemove.filter((r) => memberRoles.includes(r)).filter((r) => !rolesToAdd.includes(r));
- rolesToAdd = rolesToAdd.filter((r) => !memberRoles.includes(r));
+
+ const fullRoleList: string[] = selectedRoles.flat();
+
+ const memberRoles = (interaction.member.roles as GuildMemberRoleManager).cache.map((r) => r.id); // IDs
+ let rolesToRemove = config.roleMenu.options.map((o) => o.options.map((o) => o.role)).flat(); // IDs
+ rolesToRemove = rolesToRemove.filter((r) => memberRoles.includes(r)).filter((r) => !fullRoleList.includes(r)) // IDs
+ let roleObjectsToAdd = fullRoleList.map((r) => interaction.guild!.roles.cache.get(r)) // Role objects
+ .filter((r) => r !== undefined) as Role[];
+ roleObjectsToAdd = roleObjectsToAdd.filter((r) => !memberRoles.includes(r.id)); // Role objects
try {
- await interaction.member.roles.remove(rolesToRemove);
- await interaction.member.roles.add(rolesToAdd);
+ roleException(interaction.guild.id, interaction.user.id)
+ await (interaction.member.roles as GuildMemberRoleManager).remove(rolesToRemove);
+ await (interaction.member.roles as GuildMemberRoleManager).add(roleObjectsToAdd);
} catch (e) {
return await interaction.reply({
embeds: [
@@ -217,7 +249,7 @@
embeds: [
new EmojiEmbed()
.setTitle("Roles")
- .setDescription("Roles have been added. You may close this message.")
+ .setDescription("Roles have been updated")
.setStatus("Success")
.setEmoji("GUILD.GREEN")
],
diff --git a/src/actions/tickets/create.ts b/src/actions/tickets/create.ts
index 595a5b3..3c2dd2c 100644
--- a/src/actions/tickets/create.ts
+++ b/src/actions/tickets/create.ts
@@ -3,16 +3,16 @@
import client from "../../utils/client.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
+import { getCommandMentionByName } from "../../utils/getCommandMentionByName.js";
function capitalize(s: string) {
s = s.replace(/([A-Z])/g, " $1");
- return s.length < 3 ? s.toUpperCase() : s[0].toUpperCase() + s.slice(1).toLowerCase();
+ return s.length < 3 ? s.toUpperCase() : s[0]!.toUpperCase() + s.slice(1).toLowerCase();
}
export default async function (interaction: CommandInteraction | ButtonInteraction) {
if (!interaction.guild) return;
const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger;
-
const config = await client.database.guilds.read(interaction.guild.id);
if (!config.tickets.enabled || !config.tickets.category) {
return await interaction.reply({
@@ -21,7 +21,7 @@
.setTitle("Tickets are disabled")
.setDescription("Please enable tickets in the configuration to use this command.")
.setFooter({
- text: interaction.member.permissions.has("MANAGE_GUILD")
+ text: (interaction.member!.permissions as Discord.PermissionsBitField).has("ManageGuild")
? "You can enable it by running /settings tickets"
: ""
})
@@ -31,16 +31,26 @@
ephemeral: true
});
}
- const category = interaction.guild.channels.cache.get(config.tickets.category) as Discord.CategoryChannel;
let count = 0;
- category.children.forEach((element) => {
- if (!(element.type === "GUILD_TEXT")) return;
- if ((element as Discord.TextChannel).topic.includes(`${interaction.member.user.id}`)) {
- if ((element as Discord.TextChannel).topic.endsWith("Active")) {
- count++;
+ const targetChannel: Discord.CategoryChannel | Discord.TextChannel = (await interaction.guild.channels.fetch(config.tickets.category))! as Discord.CategoryChannel | Discord.TextChannel;
+ if (targetChannel.type === Discord.ChannelType.GuildCategory) {
+ // For channels, the topic is the user ID, then the word Active
+ const category = targetChannel as Discord.CategoryChannel;
+ category.children.cache.forEach((element) => {
+ if (!(element.type === Discord.ChannelType.GuildText)) return;
+ if (((element as Discord.TextChannel).topic ?? "").includes(`${interaction.member!.user.id}`)) {
+ if (((element as Discord.TextChannel).topic ?? "").endsWith("Active")) { count++; }
}
- }
- });
+ });
+ } else {
+ // For threads, the name is the users name, id, then the word Active
+ const channel = targetChannel as Discord.TextChannel;
+ channel.threads.cache.forEach((element: Discord.ThreadChannel) => {
+ if (element.name.includes(`${interaction.member!.user.id}`)) {
+ if (element.name.endsWith("Active")) { count++; }
+ }
+ });
+ }
if (count >= config.tickets.maxTickets) {
return await interaction.reply({
embeds: [
@@ -55,15 +65,15 @@
ephemeral: true
});
}
- let ticketTypes;
+ let ticketTypes: string[];
let custom = false;
if (config.tickets.customTypes && config.tickets.useCustom) {
ticketTypes = config.tickets.customTypes;
custom = true;
} else if (config.tickets.types) ticketTypes = toHexArray(config.tickets.types, tickets);
else ticketTypes = [];
- let chosenType;
- let splitFormattedTicketTypes = [];
+ let chosenType: string | null;
+ let splitFormattedTicketTypes: ActionRowBuilder<ButtonBuilder>[] = [];
if (ticketTypes.length > 0) {
let formattedTicketTypes = [];
formattedTicketTypes = ticketTypes.map((type) => {
@@ -78,7 +88,7 @@
}
});
for (let i = 0; i < formattedTicketTypes.length; i += 5) {
- splitFormattedTicketTypes.push(new ActionRowBuilder().addComponents(formattedTicketTypes.slice(i, i + 5)));
+ splitFormattedTicketTypes.push(new ActionRowBuilder<ButtonBuilder>().addComponents(formattedTicketTypes.slice(i, i + 5)));
}
const m = await interaction.reply({
embeds: [
@@ -94,7 +104,10 @@
});
let component;
try {
- component = await m.awaitMessageComponent({ time: 300000 });
+ component = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
return;
}
@@ -118,7 +131,7 @@
}
});
for (let i = 0; i < formattedTicketTypes.length; i += 5) {
- splitFormattedTicketTypes.push(new ActionRowBuilder().addComponents(formattedTicketTypes.slice(i, i + 5)));
+ splitFormattedTicketTypes.push(new ActionRowBuilder<ButtonBuilder>().addComponents(formattedTicketTypes.slice(i, i + 5)));
}
component.update({
embeds: [
@@ -138,107 +151,190 @@
components: splitFormattedTicketTypes
});
}
- const overwrites = [
- {
- id: interaction.member,
- allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "ATTACH_FILES", "ADD_REACTIONS", "READ_MESSAGE_HISTORY"],
- type: "member"
- }
- ] as Discord.OverwriteResolvable[];
- overwrites.push({
- id: interaction.guild.roles.everyone,
- deny: ["VIEW_CHANNEL"],
- type: "role"
- });
- if (config.tickets.supportRole !== null) {
+ let c: Discord.TextChannel | Discord.PrivateThreadChannel;
+ if (targetChannel.type === Discord.ChannelType.GuildCategory) {
+ const overwrites = [
+ {
+ id: interaction.member,
+ allow: ["ViewChannel", "SendMessages", "AttachFiles", "AddReactions", "ReadMessageHistory"],
+ type: Discord.OverwriteType.Member
+ }
+ ] as Discord.OverwriteResolvable[];
overwrites.push({
- id: interaction.guild.roles.cache.get(config.tickets.supportRole),
- allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "ATTACH_FILES", "ADD_REACTIONS", "READ_MESSAGE_HISTORY"],
- type: "role"
+ id: interaction.guild.roles.everyone,
+ deny: ["ViewChannel"],
+ type: Discord.OverwriteType.Role
});
- }
+ if (config.tickets.supportRole !== null) {
+ overwrites.push({
+ id: interaction.guild.roles.cache.get(config.tickets.supportRole)!,
+ allow: ["ViewChannel", "SendMessages", "AttachFiles", "AddReactions", "ReadMessageHistory"],
+ type: Discord.OverwriteType.Role
+ });
+ }
- let c;
- try {
- c = await interaction.guild.channels.create(interaction.member.user.username, {
- type: "GUILD_TEXT",
- topic: `${interaction.member.user.id} Active`,
- parent: config.tickets.category,
- nsfw: false,
- permissionOverwrites: overwrites as Discord.OverwriteResolvable[],
- reason: "Creating ticket"
- });
- } catch (e) {
- return await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setTitle("Create Ticket")
- .setDescription("Failed to create ticket")
- .setStatus("Danger")
- .setEmoji("CONTROL.BLOCKCROSS")
- ]
- });
- }
- try {
- await c.send({
- content:
- `<@${interaction.member.user.id}>` +
- (config.tickets.supportRole !== null ? ` • <@&${config.tickets.supportRole}>` : ""),
- allowedMentions: {
- users: [(interaction.member as Discord.GuildMember).id],
- roles: config.tickets.supportRole !== null ? [config.tickets.supportRole] : []
+ try {
+ c = await interaction.guild.channels.create({
+ name: `${interaction.member!.user.username.toLowerCase()}`,
+ type: Discord.ChannelType.GuildText,
+ topic: `${interaction.member!.user.id} Active`,
+ parent: config.tickets.category,
+ nsfw: false,
+ permissionOverwrites: overwrites as Discord.OverwriteResolvable[],
+ reason: "Creating ticket"
+ });
+ } catch (e) {
+ return await interaction.editReply({
+ embeds: [
+ new EmojiEmbed()
+ .setTitle("Create Ticket")
+ .setDescription("Failed to create ticket")
+ .setStatus("Danger")
+ .setEmoji("CONTROL.BLOCKCROSS")
+ ]
+ });
+ }
+ try {
+ await c.send({
+ content:
+ `<@${interaction.member!.user.id}>` +
+ (config.tickets.supportRole !== null ? ` • <@&${config.tickets.supportRole}>` : ""),
+ allowedMentions: {
+ users: [(interaction.member as Discord.GuildMember).id],
+ roles: config.tickets.supportRole !== null ? [config.tickets.supportRole] : []
+ }
+ });
+ let content: string | null = null;
+ if (interaction.isCommand()) {
+ content = interaction.options.get("message")?.value as string;
}
- });
- let content = interaction.options ? interaction.options.getString("message") || "" : "";
- if (content) content = `**Message:**\n> ${content}\n`;
- const emoji = custom ? "" : getEmojiByName("TICKETS." + chosenType.toUpperCase());
- await c.send({
- embeds: [
- new EmojiEmbed()
- .setTitle("New Ticket")
- .setDescription(
- `Ticket created by <@${interaction.member.user.id}>\n` +
- `**Support type:** ${
- chosenType !== null ? emoji + " " + capitalize(chosenType) : "General"
- }\n` +
- `**Ticket ID:** \`${c.id}\`\n${content}\n` +
- "Type `/ticket close` to close this ticket."
- )
- .setStatus("Success")
- .setEmoji("GUILD.TICKET.OPEN")
- ],
- components: [
- new ActionRowBuilder().addComponents([
- new ButtonBuilder()
- .setLabel("Close")
- .setStyle(ButtonStyle.Danger)
- .setCustomId("closeticket")
- .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
- ])
- ]
- });
- const data = {
- meta: {
- type: "ticketCreate",
- displayName: "Ticket Created",
- calculateType: "ticketUpdate",
- color: NucleusColors.green,
- emoji: "GUILD.TICKET.OPEN",
- timestamp: new Date().getTime()
- },
- list: {
- ticketFor: entry(interaction.member.user.id, renderUser(interaction.member.user)),
- created: entry(new Date().getTime(), renderDelta(new Date().getTime())),
- ticketChannel: entry(c.id, renderChannel(c))
- },
- hidden: {
- guild: interaction.guild.id
+ if (content) content = `**Message:**\n> ${content}\n`;
+ let emoji;
+ if (chosenType) {
+ emoji = custom ? "" : getEmojiByName("TICKETS." + chosenType.toUpperCase());
+ } else {
+ emoji = "";
}
- };
- log(data);
- } catch (e) {
- console.log(e);
+ await c.send({
+ embeds: [
+ new EmojiEmbed()
+ .setTitle("New Ticket")
+ .setDescription(
+ `Ticket created by <@${interaction.member!.user.id}>\n` +
+ `**Support type:** ${
+ chosenType !== null ? emoji + " " + capitalize(chosenType) : "General"
+ }\n` +
+ `**Ticket ID:** \`${c.id}\`\n${content ?? ""}\n` +
+ `Type ${await getCommandMentionByName("ticket/close")} to close this ticket.`
+ )
+ .setStatus("Success")
+ .setEmoji("GUILD.TICKET.OPEN")
+ ],
+ components: [
+ new ActionRowBuilder<ButtonBuilder>().addComponents([
+ new ButtonBuilder()
+ .setLabel("Close")
+ .setStyle(ButtonStyle.Danger)
+ .setCustomId("closeticket")
+ .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
+ ])
+ ]
+ });
+ } catch (e) {
+ return await interaction.editReply({
+ embeds: [
+ new EmojiEmbed()
+ .setTitle("Create Ticket")
+ .setDescription("Failed to create ticket")
+ .setStatus("Danger")
+ .setEmoji("GUILD.TICKET.CLOSE")
+ ]
+ });
+ }
+ } else {
+ c = await targetChannel.threads.create({name: `${interaction.member!.user.username} - ${interaction.member!.user.id} - Active`,
+ autoArchiveDuration: 60 * 24 * 7,
+ type: Discord.ChannelType.PrivateThread,
+ reason: "Creating ticket"
+ }) as Discord.PrivateThreadChannel;
+ c.members.add(interaction.member!.user.id); // TODO: When a thread is used, and a support role is added, automatically set channel permissions
+ try {
+ await c.send({
+ content:
+ `<@${interaction.member!.user.id}>` +
+ (config.tickets.supportRole !== null ? ` • <@&${config.tickets.supportRole}>` : ""),
+ allowedMentions: {
+ users: [(interaction.member as Discord.GuildMember).id],
+ roles: config.tickets.supportRole !== null ? [config.tickets.supportRole] : []
+ }
+ });
+ let content: string | null = null;
+ if (interaction.isCommand()) {
+ content = interaction.options.get("message")?.value as string;
+ }
+ if (content) content = `**Message:**\n> ${content}\n`;
+ let emoji;
+ if (chosenType) {
+ emoji = custom ? "" : getEmojiByName("TICKETS." + chosenType.toUpperCase());
+ } else {
+ emoji = "";
+ }
+ await c.send({
+ embeds: [
+ new EmojiEmbed()
+ .setTitle("New Ticket")
+ .setDescription(
+ `Ticket created by <@${interaction.member!.user.id}>\n` +
+ `**Support type:** ${
+ chosenType !== null ? emoji + " " + capitalize(chosenType) : "General"
+ }\n` +
+ `**Ticket ID:** \`${c.id}\`\n${content ?? ""}\n` +
+ `Type ${await getCommandMentionByName("ticket/close")} to close this ticket.`
+ )
+ .setStatus("Success")
+ .setEmoji("GUILD.TICKET.OPEN")
+ ],
+ components: [
+ new ActionRowBuilder<ButtonBuilder>().addComponents([
+ new ButtonBuilder()
+ .setLabel("Close")
+ .setStyle(ButtonStyle.Danger)
+ .setCustomId("closeticket")
+ .setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
+ ])
+ ]
+ });
+ } catch (e) {
+ return await interaction.editReply({
+ embeds: [
+ new EmojiEmbed()
+ .setTitle("Create Ticket")
+ .setDescription("Failed to create ticket")
+ .setStatus("Danger")
+ .setEmoji("GUILD.TICKET.CLOSE")
+ ]
+ });
+ }
}
+ const data = {
+ meta: {
+ type: "ticketCreate",
+ displayName: "Ticket Created",
+ calculateType: "ticketUpdate",
+ color: NucleusColors.green,
+ emoji: "GUILD.TICKET.OPEN",
+ timestamp: new Date().getTime()
+ },
+ list: {
+ ticketFor: entry(interaction.member!.user.id, renderUser(interaction.member!.user! as Discord.User)),
+ created: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+ ticketChannel: entry(c.id, renderChannel(c))
+ },
+ hidden: {
+ guild: interaction.guild.id
+ }
+ };
+ log(data);
await interaction.editReply({
embeds: [
new EmojiEmbed()
diff --git a/src/actions/tickets/delete.ts b/src/actions/tickets/delete.ts
index cab38ec..48d2a51 100644
--- a/src/actions/tickets/delete.ts
+++ b/src/actions/tickets/delete.ts
@@ -1,50 +1,52 @@
-import Discord, { ButtonBuilder, ActionRowBuilder, ButtonStyle, ButtonInteraction } from "discord.js";
+import { getCommandMentionByName } from '../../utils/getCommandMentionByName.js';
+import Discord, { ActionRowBuilder, ButtonBuilder, ButtonInteraction, PrivateThreadChannel, TextChannel, ButtonStyle } from "discord.js";
import client from "../../utils/client.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
+import { preloadPage } from '../../utils/createTemporaryStorage.js';
export default async function (interaction: Discord.CommandInteraction | ButtonInteraction) {
if (!interaction.guild) return;
+ const config = await client.database.guilds.read(interaction.guild.id);
const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger;
- const config = await client.database.guilds.read(interaction.guild.id);
- let thread = false;
- if (interaction.channel instanceof Discord.ThreadChannel) thread = true;
- const threadChannel = interaction.channel as Discord.ThreadChannel;
- const channel = interaction.channel as Discord.TextChannel;
- if (
- !channel.parent ||
- config.tickets.category !== channel.parent.id ||
- (thread ? threadChannel.parent.parent.id !== config.tickets.category : false)
- ) {
- return interaction.reply({
+ const ticketChannel = config.tickets.category;
+ if (!("parent" in interaction.channel!)) {
+ return await interaction.reply({
embeds: [
new EmojiEmbed()
- .setTitle("Deleting Ticket...")
- .setDescription(
- "This ticket is not in your tickets category, so cannot be deleted. You cannot run close in a thread."
- )
+ .setTitle("Not a ticket")
+ .setDescription("This channel isn't a ticket, so you can't delete it.")
.setStatus("Danger")
.setEmoji("CONTROL.BLOCKCROSS")
- ],
- ephemeral: true
+ ], ephemeral: true
});
- }
- const status = channel.topic.split(" ")[1];
- if (status === "Archived") {
- await interaction.reply({
+ } else if (interaction.channel!.parent!.id !== ticketChannel) {
+ return await interaction.reply({
embeds: [
new EmojiEmbed()
- .setTitle("Delete Ticket")
- .setDescription("Your ticket is being deleted...")
+ .setTitle("Not a ticket")
+ .setDescription("This channel isn't a ticket, so you can't delete it.")
.setStatus("Danger")
- .setEmoji("GUILD.TICKET.CLOSE")
- ]
+ .setEmoji("CONTROL.BLOCKCROSS")
+ ], ephemeral: true
});
+ }
+ const channel: PrivateThreadChannel | TextChannel = interaction.channel as PrivateThreadChannel | TextChannel;
+ let status: string | null = ("topic" in interaction.channel) ? interaction.channel!.topic : interaction.channel.name;
+ status = status ?? "";
+ if (status.endsWith("Archived")) { status = "Archived"; }
+ else { status = "Active"; }
+
+ const uID = channel.type === Discord.ChannelType.PrivateThread ? channel.name.split(" - ")[1] : channel.topic!.split(" ")[0];
+
+ if (status === "Archived") {
+ // Delete the ticket
+
const data = {
meta: {
- type: "ticketDeleted",
- displayName: "Ticket Deleted",
+ type: "ticketClosed",
+ displayName: "Ticket Closed",
calculateType: "ticketUpdate",
color: NucleusColors.red,
emoji: "GUILD.TICKET.CLOSE",
@@ -52,85 +54,45 @@
},
list: {
ticketFor: entry(
- channel.topic.split(" ")[0],
- renderUser((await interaction.guild.members.fetch(channel.topic.split(" ")[0])).user)
+ uID!,
+ renderUser((await interaction.guild.members.fetch(uID!)).user)
),
- deletedBy: entry(interaction.member.user.id, renderUser(interaction.member.user)),
- deleted: entry(new Date().getTime(), renderDelta(new Date().getTime()))
+ closedBy: entry(interaction.member!.user.id, renderUser(interaction.member!.user as Discord.User)),
+ closed: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+ ticketChannel: entry(channel.id, channel.name)
},
hidden: {
guild: interaction.guild.id
}
};
log(data);
- interaction.channel.delete();
- return;
+
+ await channel.delete();
} else if (status === "Active") {
+ // Close the ticket
+
+ if (channel.isThread()) {
+ channel.setName(`${channel.name.replace("Active", "Archived")}`);
+ channel.members.remove(channel.name.split(" - ")[1]!);
+ } else {
+ channel.setTopic(`${(channel.topic ?? "").replace("Active", "Archived")}`);
+ if (!channel.topic!.includes("Archived")) { channel.setTopic("0 Archived"); }
+ await channel.permissionOverwrites.delete(channel.topic!.split(" ")[0]!);
+ }
+ preloadPage(interaction.channel.id, "privacy", "2")
await interaction.reply({
embeds: [
new EmojiEmbed()
- .setTitle("Close Ticket")
- .setDescription("Your ticket is being closed...")
- .setStatus("Warning")
- .setEmoji("GUILD.TICKET.ARCHIVED")
- ]
- });
- const overwrites = [
- {
- id: channel.topic.split(" ")[0],
- deny: ["VIEW_CHANNEL"],
- type: "member"
- },
- {
- id: interaction.guild.id,
- deny: ["VIEW_CHANNEL"],
- type: "role"
- }
- ] as Discord.OverwriteResolvable[];
- if (config.tickets.supportRole !== null) {
- overwrites.push({
- id: interaction.guild.roles.cache.get(config.tickets.supportRole),
- allow: ["VIEW_CHANNEL", "SEND_MESSAGES", "ATTACH_FILES", "ADD_REACTIONS", "READ_MESSAGE_HISTORY"],
- type: "role"
- });
- }
- channel.edit({ permissionOverwrites: overwrites });
- channel.setTopic(`${channel.topic.split(" ")[0]} Archived`);
- const data = {
- meta: {
- type: "ticketClosed",
- displayName: "Ticket Closed",
- calculateType: "ticketUpdate",
- color: NucleusColors.yellow,
- emoji: "GUILD.TICKET.ARCHIVED",
- timestamp: new Date().getTime()
- },
- list: {
- ticketFor: entry(
- channel.topic.split(" ")[0],
- renderUser((await interaction.guild.members.fetch(channel.topic.split(" ")[0])).user)
- ),
- closedBy: entry(interaction.member.user.id, renderUser(interaction.member.user)),
- closed: entry(new Date().getTime(), renderDelta(new Date().getTime())),
- ticketChannel: entry(channel.id, renderChannel(channel))
- },
- hidden: {
- guild: interaction.guild.id
- }
- };
- log(data);
- await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setTitle("Close Ticket")
- .setDescription(
- "This ticket has been closed.\nType `/ticket close` again to delete it.\n\nNote: Check `/privacy` for details about transcripts."
- )
+ .setTitle("Archived Ticket")
+ .setDescription(`This ticket has been Archived. Type ${await getCommandMentionByName("ticket/close")} to delete it.` +
+ await client.database.premium.hasPremium(interaction.guild.id) ?
+ `\n\nFor more info on transcripts, check ${await getCommandMentionByName("privacy")}` :
+ "")
.setStatus("Warning")
.setEmoji("GUILD.TICKET.ARCHIVED")
],
components: [
- new ActionRowBuilder().addComponents(
+ new ActionRowBuilder<ButtonBuilder>().addComponents(
[
new ButtonBuilder()
.setLabel("Delete")
@@ -138,27 +100,52 @@
.setCustomId("closeticket")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
].concat(
- client.database.premium.hasPremium(interaction.guild.id)
+ await client.database.premium.hasPremium(interaction.guild.id)
? [
- new ButtonBuilder()
- .setLabel("Create Transcript and Delete")
- .setStyle(ButtonStyle.Primary)
- .setCustomId("createtranscript")
- .setEmoji(getEmojiByName("CONTROL.DOWNLOAD", "id"))
- ]
+ new ButtonBuilder()
+ .setLabel("Create Transcript and Delete")
+ .setStyle(ButtonStyle.Primary)
+ .setCustomId("createtranscript")
+ .setEmoji(getEmojiByName("CONTROL.DOWNLOAD", "id"))
+ ]
: []
)
)
]
});
- return;
+ const data = {
+ meta: {
+ type: "ticketClosed",
+ displayName: "Ticket Archived",
+ calculateType: "ticketUpdate",
+ color: NucleusColors.yellow,
+ emoji: "GUILD.TICKET.ARCHIVED",
+ timestamp: new Date().getTime()
+ },
+ list: {
+ ticketFor: entry(
+ uID!,
+ renderUser((await interaction.guild.members.fetch(uID!)).user)
+ ),
+ archivedBy: entry(interaction.member!.user.id, renderUser(interaction.member!.user as Discord.User)),
+ archived: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+ ticketChannel: entry(channel.id, renderChannel(channel))
+ },
+ hidden: {
+ guild: interaction.guild.id
+ }
+ };
+ log(data);
}
+ return;
}
-async function purgeByUser(member, guild) {
+
+async function purgeByUser(member: string, guild: string) {
const config = await client.database.guilds.read(guild.id);
+ const fetchedGuild = await client.guilds.fetch(guild);
if (!config.tickets.category) return;
- const tickets = guild.channels.cache.get(config.tickets.category);
+ const tickets = fetchedGuild.channels.cache.get(config.tickets.category);
if (!tickets) return;
const ticketChannels = tickets.children;
let deleted = 0;
@@ -198,4 +185,4 @@
}
}
-export { purgeByUser };
+export { purgeByUser };
\ No newline at end of file
diff --git a/src/commands/help.ts b/src/commands/help.ts
index c9bba7d..767ca46 100644
--- a/src/commands/help.ts
+++ b/src/commands/help.ts
@@ -8,13 +8,13 @@
const callback = async (interaction: CommandInteraction): Promise<void> => {
interaction.reply({components: [new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
- .setLabel("Verify")
+ .setLabel("Create ticket")
.setStyle(ButtonStyle.Primary)
- .setCustomId("verifybutton")
+ .setCustomId("createticket")
)]}); // TODO: FINISH THIS FOR RELEASE
};
-const check = (_interaction: CommandInteraction) => {
+const check = () => {
return true;
};
diff --git a/src/commands/mod/about.ts b/src/commands/mod/about.ts
index c69f4a9..130cdbc 100644
--- a/src/commands/mod/about.ts
+++ b/src/commands/mod/about.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from '../../utils/defaultEmbeds.js';
+import { LoadingEmbed } from '../../utils/defaults.js';
import type { HistorySchema } from "../../utils/database.js";
import Discord, {
CommandInteraction,
@@ -251,7 +251,10 @@
}
let i: MessageComponentInteraction;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
interaction.editReply({
embeds: [
@@ -354,7 +357,10 @@
})) as Message;
let i: MessageComponentInteraction;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
continue;
@@ -426,7 +432,7 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as GuildMember;
if (!member.permissions.has("ModerateMembers"))
- throw new Error("You do not have the *Moderate Members* permission");
+ return "You do not have the *Moderate Members* permission";
return true;
};
diff --git a/src/commands/mod/ban.ts b/src/commands/mod/ban.ts
index 330edfd..18b6c7e 100644
--- a/src/commands/mod/ban.ts
+++ b/src/commands/mod/ban.ts
@@ -5,7 +5,7 @@
import keyValueList from "../../utils/generateKeyValueList.js";
import addPlurals from "../../utils/plurals.js";
import client from "../../utils/client.js";
-import { LinkWarningFooter } from "../../utils/defaultEmbeds.js";
+import { LinkWarningFooter } from "../../utils/defaults.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
@@ -84,7 +84,7 @@
.setEmoji("PUNISH.BAN.RED")
.setTitle("Banned")
.setDescription(
- `You have been banned in ${interaction.guild.name}` +
+ `You have been banned from ${interaction.guild.name}` +
(reason ? ` for:\n${reason}` : ".\n*No reason was provided.*")
)
.setStatus("Danger")
@@ -131,7 +131,7 @@
banned: entry(new Date().getTime().toString(), 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.toString(), renderDelta(member.user.createdAt.getTime())),
+ accountCreated: entry(member.user.createdTimestamp, renderDelta(member.user.createdTimestamp)),
serverMemberCount: interaction.guild.memberCount
},
hidden: {
diff --git a/src/commands/mod/kick.ts b/src/commands/mod/kick.ts
index a4b9774..bdbb9ee 100644
--- a/src/commands/mod/kick.ts
+++ b/src/commands/mod/kick.ts
@@ -1,4 +1,4 @@
-import { LinkWarningFooter } from './../../utils/defaultEmbeds.js';
+import { LinkWarningFooter } from '../../utils/defaults.js';
import { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
// @ts-expect-error
import humanizeDuration from "humanize-duration";
@@ -177,19 +177,19 @@
const mePos = me.roles.cache.size > 1 ? me.roles.highest.position : 0;
const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0;
// Do not allow kicking the owner
- if (member.id === interaction.guild.ownerId) throw new Error("You cannot kick the owner of the server");
+ if (member.id === interaction.guild.ownerId) return "You cannot kick the owner of the server";
// Check if Nucleus can kick the member
- if (!(mePos > applyPos)) throw new Error("I do not have a role higher than that member");
+ if (!(mePos > applyPos)) return "I do not have a role higher than that member";
// Check if Nucleus has permission to kick
- if (!me.permissions.has("KickMembers")) throw new Error("I do not have the *Kick Members* permission");
+ if (!me.permissions.has("KickMembers")) return "I do not have the *Kick Members* permission";
// Do not allow kicking Nucleus
- if (member.id === interaction.guild.members.me!.id) throw new Error("I cannot kick myself");
+ if (member.id === interaction.guild.members.me!.id) return "I cannot kick myself";
// Allow the owner to kick anyone
if (member.id === interaction.guild.ownerId) return true;
// Check if the user has kick_members permission
- if (!member.permissions.has("KickMembers")) throw new Error("You do not have the *Kick Members* permission");
+ if (!member.permissions.has("KickMembers")) return "You do not have the *Kick Members* permission";
// Check if the user is below on the role list
- if (!(memberPos > applyPos)) throw new Error("You do not have a role higher than that member");
+ if (!(memberPos > applyPos)) return "You do not have a role higher than that member";
// Allow kick
return true;
};
diff --git a/src/commands/mod/mute.ts b/src/commands/mod/mute.ts
index b558e87..3270d37 100644
--- a/src/commands/mod/mute.ts
+++ b/src/commands/mod/mute.ts
@@ -1,4 +1,4 @@
-import { LinkWarningFooter, LoadingEmbed } from "./../../utils/defaultEmbeds.js";
+import { LinkWarningFooter, LoadingEmbed } from "../../utils/defaults.js";
import Discord, { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
@@ -370,20 +370,20 @@
const mePos = me.roles.cache.size > 1 ? me.roles.highest.position : 0;
const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0;
// Do not allow muting the owner
- if (member.id === interaction.guild.ownerId) throw new Error("You cannot mute the owner of the server");
+ if (member.id === interaction.guild.ownerId) return "You cannot mute the owner of the server";
// Check if Nucleus can mute the member
- if (!(mePos > applyPos)) throw new Error("I do not have a role higher than that member");
+ if (!(mePos > applyPos)) return "I do not have a role higher than that member";
// Check if Nucleus has permission to mute
- if (!me.permissions.has("ModerateMembers")) throw new Error("I do not have the *Moderate Members* permission");
+ if (!me.permissions.has("ModerateMembers")) return "I do not have the *Moderate Members* permission";
// Do not allow muting Nucleus
- if (member.id === me.id) throw new Error("I cannot mute myself");
+ if (member.id === me.id) return "I cannot mute myself";
// Allow the owner to mute anyone
if (member.id === interaction.guild.ownerId) return true;
// Check if the user has moderate_members permission
if (!member.permissions.has("ModerateMembers"))
- throw new Error("You do not have the *Moderate Members* permission");
+ return "You do not have the *Moderate Members* permission";
// Check if the user is below on the role list
- if (!(memberPos > applyPos)) throw new Error("You do not have a role higher than that member");
+ if (!(memberPos > applyPos)) return "You do not have a role higher than that member";
// Allow mute
return true;
};
diff --git a/src/commands/mod/nick.ts b/src/commands/mod/nick.ts
index c5e45b6..1fbde64 100644
--- a/src/commands/mod/nick.ts
+++ b/src/commands/mod/nick.ts
@@ -176,20 +176,20 @@
const applyPos = apply.roles.cache.size ? apply.roles.highest.position : 0;
if (!interaction.guild) return false;
// Do not allow any changing of the owner
- if (member.id === interaction.guild.ownerId) throw new Error("You cannot change the owner's nickname");
+ if (member.id === interaction.guild.ownerId) return "You cannot change the owner's nickname";
// Check if Nucleus can change the nickname
- if (!(mePos > applyPos)) throw new Error("I do not have a role higher than that member");
+ if (!(mePos > applyPos)) return "I do not have a role higher than that member";
// Check if Nucleus has permission to change the nickname
- if (!me.permissions.has("ManageNicknames")) throw new Error("I do not have the *Manage Nicknames* permission");
+ if (!me.permissions.has("ManageNicknames")) return "I do not have the *Manage Nicknames* permission";
// Allow the owner to change anyone's nickname
if (member.id === interaction.guild.ownerId) return true;
// Check if the user has manage_nicknames permission
if (!member.permissions.has("ManageNicknames"))
- throw new Error("You do not have the *Manage Nicknames* permission");
+ return "You do not have the *Manage Nicknames* permission";
// Allow changing your own nickname
if (member === apply) return true;
// Check if the user is below on the role list
- if (!(memberPos > applyPos)) throw new Error("You do not have a role higher than that member");
+ if (!(memberPos > applyPos)) return "You do not have a role higher than that member";
// Allow change
return true;
};
diff --git a/src/commands/mod/purge.ts b/src/commands/mod/purge.ts
index 5425714..e6b4670 100644
--- a/src/commands/mod/purge.ts
+++ b/src/commands/mod/purge.ts
@@ -1,3 +1,4 @@
+import { JSONTranscriptFromMessageArray, JSONTranscriptToHumanReadable } from '../../utils/logTranscripts.js';
import Discord, { CommandInteraction, GuildChannel, GuildMember, TextChannel, ButtonStyle, ButtonBuilder } from "discord.js";
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import confirmationMessage from "../../utils/confirmationMessage.js";
@@ -93,7 +94,7 @@
let component;
try {
component = m.awaitMessageComponent({
- filter: (m) => m.user.id === interaction.user.id,
+ filter: (m) => m.user.id === interaction.user.id && m.channel!.id === interaction.channel!.id,
time: 300000
});
} catch (e) {
@@ -146,7 +147,7 @@
displayName: "Channel Purged",
calculateType: "messageDelete",
color: NucleusColors.red,
- emoji: "PUNISH.BAN.RED",
+ emoji: "CHANNEL.PURGE.RED",
timestamp: new Date().getTime()
},
list: {
@@ -160,19 +161,9 @@
}
};
log(data);
- let out = "";
- deleted.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";
- });
+ const transcript = JSONTranscriptToHumanReadable(JSONTranscriptFromMessageArray(deleted)!);
const attachmentObject = {
- attachment: Buffer.from(out),
+ attachment: Buffer.from(transcript),
name: `purge-${channel.id}-${Date.now()}.txt`,
description: "Purge log"
};
@@ -197,7 +188,7 @@
let component;
try {
component = await m.awaitMessageComponent({
- filter: (m) => m.user.id === interaction.user.id,
+ filter: (m) => m.user.id === interaction.user.id && m.channel!.id === interaction.channel!.id,
time: 300000
});
} catch {
@@ -304,7 +295,7 @@
displayName: "Channel Purged",
calculateType: "messageDelete",
color: NucleusColors.red,
- emoji: "PUNISH.BAN.RED",
+ emoji: "CHANNEL.PURGE.RED",
timestamp: new Date().getTime()
},
list: {
@@ -367,7 +358,7 @@
let component;
try {
component = await m.awaitMessageComponent({
- filter: (m) => m.user.id === interaction.user.id,
+ filter: (m) => m.user.id === interaction.user.id && m.channel!.id === interaction.channel!.id,
time: 300000
});
} catch {
@@ -405,11 +396,11 @@
const member = interaction.member as GuildMember;
const me = interaction.guild.members.me!;
// Check if nucleus has the manage_messages permission
- if (!me.permissions.has("ManageMessages")) throw new Error("I do not have the *Manage Messages* permission");
+ if (!me.permissions.has("ManageMessages")) return "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("ManageMessages")) throw new Error("You do not have the *Manage Messages* permission");
+ if (!member.permissions.has("ManageMessages")) return "You do not have the *Manage Messages* permission";
// Allow purge
return true;
};
diff --git a/src/commands/mod/slowmode.ts b/src/commands/mod/slowmode.ts
index 9bd994d..9792827 100644
--- a/src/commands/mod/slowmode.ts
+++ b/src/commands/mod/slowmode.ts
@@ -79,9 +79,9 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as GuildMember;
// Check if Nucleus can set the slowmode
- if (!interaction.guild!.members.me!.permissions.has("ManageChannels")) throw new Error("I do not have the *Manage Channels* permission");
+ if (!interaction.guild!.members.me!.permissions.has("ManageChannels")) return "I do not have the *Manage Channels* permission";
// Check if the user has manage_channel permission
- if (!member.permissions.has("ManageChannels")) throw new Error("You do not have the *Manage Channels* permission");
+ if (!member.permissions.has("ManageChannels")) return "You do not have the *Manage Channels* permission";
// Allow slowmode
return true;
};
diff --git a/src/commands/mod/softban.ts b/src/commands/mod/softban.ts
index 12bfc3e..35f275f 100644
--- a/src/commands/mod/softban.ts
+++ b/src/commands/mod/softban.ts
@@ -1,173 +1,201 @@
-import { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
+import Discord, { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, User, ButtonStyle } from "discord.js";
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import confirmationMessage from "../../utils/confirmationMessage.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import keyValueList from "../../utils/generateKeyValueList.js";
+import addPlurals from "../../utils/plurals.js";
import client from "../../utils/client.js";
-import addPlural from "../../utils/plurals.js";
+import { LinkWarningFooter } from "../../utils/defaults.js";
+
const command = (builder: SlashCommandSubcommandBuilder) =>
builder
.setName("softban")
.setDescription("Kicks a user and deletes their messages")
.addUserOption((option) => option.setName("user").setDescription("The user to softban").setRequired(true))
- .addIntegerOption((option) =>
+ .addNumberOption((option) =>
option
.setName("delete")
- .setDescription("The days of messages to delete | Default: 0")
+ .setDescription("Delete this number of days of messages from the user | Default: 0")
.setMinValue(0)
.setMaxValue(7)
.setRequired(false)
);
-const callback = async (interaction: CommandInteraction): Promise<unknown> => {
+
+const callback = async (interaction: CommandInteraction): Promise<void> => {
+ if (!interaction.guild) return;
const { renderUser } = client.logger;
// TODO:[Modals] Replace this with a modal
let reason = null;
let notify = true;
let confirmation;
+ let chosen = false;
let timedOut = false;
- let success = false;
- while (!timedOut && !success) {
- const confirmation = await new confirmationMessage(interaction)
+ do {
+ confirmation = await new confirmationMessage(interaction)
.setEmoji("PUNISH.BAN.RED")
.setTitle("Softban")
.setDescription(
keyValueList({
- user: renderUser(interaction.options.getUser("user")),
- reason: reason ? "\n> " + (reason ?? "").replaceAll("\n", "\n> ") : "*No reason provided*"
+ user: renderUser(interaction.options.getUser("user")!),
+ reason: reason ? "\n> " + (reason).replaceAll("\n", "\n> ") : "*No reason provided*"
}) +
`The user **will${notify ? "" : " not"}** be notified\n` +
- `${addPlural(
- interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0,
- "day"
- )} of messages will be deleted\n\n` +
+ `${addPlurals(
+ (interaction.options.get("delete")?.value as number | null) ?? 0, "day")
+ } of messages will be deleted\n\n` +
`Are you sure you want to softban <@!${(interaction.options.getMember("user") as GuildMember).id}>?`
)
- .setColor("Danger")
.addCustomBoolean(
"notify",
"Notify user",
false,
- null,
- null,
+ undefined,
+ "The user will be sent a DM",
null,
"ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
notify
)
+ .setColor("Danger")
.addReasonButton(reason ?? "")
+ .setFailedMessage("No changes were made", "Success", "PUNISH.BAN.GREEN")
.send(reason !== null);
reason = reason ?? "";
if (confirmation.cancelled) timedOut = true;
- else if (confirmation.success) success = true;
+ else if (confirmation.success !== undefined) chosen = 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);
- try {
- if (notify) {
- await (interaction.options.getMember("user") as GuildMember).send({
- embeds: [
- new EmojiEmbed()
- .setEmoji("PUNISH.BAN.RED")
- .setTitle("Softbanned")
- .setDescription(
- `You have been softbanned from ${interaction.guild.name}` +
- (reason ? ` for:\n> ${reason}` : ".")
- )
- .setStatus("Danger")
- ],
- components: [
- new ActionRowBuilder().addComponents(
- config.moderation.ban.text
- ? [
- new ButtonBuilder()
- .setStyle(ButtonStyle.Link)
- .setLabel(config.moderation.ban.text)
- .setURL(config.moderation.ban.link)
- ]
- : []
- )
- ]
- });
- dmd = true;
- }
- } catch {
- dmd = false;
- }
- const member = interaction.options.getMember("user") as GuildMember;
- try {
- await member.ban({
- days: Number(interaction.options.getInteger("delete") ?? 0),
- reason: reason
- });
- await interaction.guild.members.unban(member, "Softban");
- } catch {
- await interaction.editReply({
+ else if (confirmation.components) notify = confirmation.components["notify"]!.active;
+ } while (!timedOut && !chosen)
+ if (timedOut || !confirmation.success) return;
+ reason = reason.length ? reason : null
+ let dmSent = false;
+ let dmMessage;
+ const config = await client.database.guilds.read(interaction.guild.id);
+ try {
+ if (notify) {
+ if (reason) { reason = reason.split("\n").map((line) => "> " + line).join("\n") }
+ const messageData: {
+ embeds: EmojiEmbed[];
+ components: ActionRowBuilder<ButtonBuilder>[];
+ } = {
embeds: [
new EmojiEmbed()
.setEmoji("PUNISH.BAN.RED")
.setTitle("Softban")
- .setDescription("Something went wrong and the user was not softbanned")
+ .setDescription(
+ `You have been softbanned from ${interaction.guild.name}` +
+ (reason ? ` for:\n${reason}` : ".\n*No reason was provided.*")
+ )
.setStatus("Danger")
],
components: []
- });
+ };
+ if (config.moderation.softban.text && config.moderation.softban.link) {
+ messageData.embeds[0]!.setFooter(LinkWarningFooter)
+ messageData.components.push(new ActionRowBuilder<Discord.ButtonBuilder>()
+ .addComponents(new ButtonBuilder()
+ .setStyle(ButtonStyle.Link)
+ .setLabel(config.moderation.softban.text)
+ .setURL(config.moderation.softban.link)
+ )
+ )
+ }
+ dmMessage = await (interaction.options.getMember("user") as GuildMember).send(messageData);
+ dmSent = true;
}
- await client.database.history.create("softban", interaction.guild.id, member.user, reason);
- const failed = !dmd && notify;
- await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setEmoji(`PUNISH.BAN.${failed ? "YELLOW" : "GREEN"}`)
- .setTitle("Softban")
- .setDescription("The member was softbanned" + (failed ? ", but could not be notified" : ""))
- .setStatus(failed ? "Warning" : "Success")
- ],
- components: []
- });
- } else {
- await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setEmoji("PUNISH.BAN.GREEN")
- .setTitle("Softban")
- .setDescription("No changes were made")
- .setStatus("Success")
- ],
- components: []
- });
+ } catch {
+ dmSent = false;
}
+ try {
+ const member = interaction.options.getMember("user") as GuildMember;
+ const days: number = interaction.options.get("delete")?.value as number | null ?? 0;
+ member.ban({
+ deleteMessageSeconds: days * 24 * 60 * 60,
+ reason: reason ?? "*No reason provided*"
+ });
+ await interaction.guild.members.unban(member, "Softban");
+ 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: "memberSoftban",
+ displayName: "Member Softbanned",
+ calculateType: "guildMemberPunish",
+ color: NucleusColors.yellow,
+ emoji: "PUNISH.BAN.YELLOW",
+ timestamp: new Date().getTime()
+ },
+ list: {
+ memberId: entry(member.user.id, `\`${member.user.id}\``),
+ name: entry(member.user.id, renderUser(member.user)),
+ softbanned: entry(new Date().getTime().toString(), renderDelta(new Date().getTime())),
+ softbannedBy: entry(interaction.user.id, renderUser(interaction.user)),
+ reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"),
+ accountCreated: entry(member.user.createdTimestamp, renderDelta(member.user.createdTimestamp)),
+ serverMemberCount: interaction.guild.memberCount
+ },
+ hidden: {
+ guild: interaction.guild.id
+ }
+ };
+ log(data);
+ } catch {
+ await interaction.editReply({
+ embeds: [
+ new EmojiEmbed()
+ .setEmoji("PUNISH.BAN.RED")
+ .setTitle("Softban")
+ .setDescription("Something went wrong and the user was not softbanned")
+ .setStatus("Danger")
+ ],
+ components: []
+ });
+ if (dmSent && dmMessage) await dmMessage.delete();
+ return;
+ }
+ const failed = !dmSent && notify;
+ await interaction.editReply({
+ embeds: [
+ new EmojiEmbed()
+ .setEmoji(`PUNISH.BAN.${failed ? "YELLOW" : "GREEN"}`)
+ .setTitle("Softban")
+ .setDescription("The member was softbanned" + (failed ? ", but could not be notified" : ""))
+ .setStatus(failed ? "Warning" : "Success")
+ ],
+ components: []
+ });
};
-const check = (interaction: CommandInteraction) => {
+const check = async (interaction: CommandInteraction) => {
+ if (!interaction.guild) return;
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 me = interaction.guild.members.me!;
+ let apply = interaction.options.getUser("user") as User | GuildMember;
const memberPos = member.roles.cache.size > 1 ? member.roles.highest.position : 0;
const mePos = me.roles.cache.size > 1 ? me.roles.highest.position : 0;
- const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0;
- // Do not allow softbanning the owner
+ let applyPos = 0
+ try {
+ apply = await interaction.guild.members.fetch(apply.id) as GuildMember
+ applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0;
+ } catch {
+ apply = apply as User
+ }
+ // Do not allow banning the owner
if (member.id === interaction.guild.ownerId) throw new Error("You cannot softban the owner of the server");
// Check if Nucleus can ban the member
if (!(mePos > applyPos)) throw new Error("I do not have a role higher than that member");
// Check if Nucleus has permission to ban
- if (!me.permissions.has("BAN_MEMBERS")) throw new Error("I do not have the *Ban Members* permission");
- // Do not allow softbanning Nucleus
+ if (!me.permissions.has("BanMembers")) throw new Error("I do not have the *Ban Members* permission");
+ // Do not allow banning Nucleus
if (member.id === me.id) throw new Error("I cannot softban myself");
- // Allow the owner to softban anyone
+ // Allow the owner to ban anyone
if (member.id === interaction.guild.ownerId) return true;
// Check if the user has ban_members permission
- if (!member.permissions.has("BAN_MEMBERS")) throw new Error("You do not have the *Ban Members* permission");
+ if (!member.permissions.has("BanMembers")) throw new Error("You do not have the *Ban Members* permission");
// Check if the user is below on the role list
if (!(memberPos > applyPos)) throw new Error("You do not have a role higher than that member");
- // Allow softban
+ // Allow ban
return true;
};
diff --git a/src/commands/mod/unban.ts b/src/commands/mod/unban.ts
index d34f303..37fee99 100644
--- a/src/commands/mod/unban.ts
+++ b/src/commands/mod/unban.ts
@@ -1,5 +1,5 @@
-import { CommandInteraction, GuildMember, User } from "discord.js";
-import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { CommandInteraction, GuildMember, User } from "discord.js";
+import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import keyValueList from "../../utils/generateKeyValueList.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
@@ -16,7 +16,7 @@
const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (!interaction.guild) return;
const bans = await interaction.guild.bans.fetch();
- const user = interaction.options.getString("user");
+ const user = interaction.options.get("user")?.value as string;
let resolved = bans.find((ban) => ban.user.id === user);
if (!resolved) resolved = bans.find((ban) => ban.user.username.toLowerCase() === user.toLowerCase());
if (!resolved) resolved = bans.find((ban) => ban.user.tag.toLowerCase() === user.toLowerCase());
@@ -48,7 +48,7 @@
try {
await interaction.guild.members.unban(resolved.user as User, "Unban");
const member = resolved.user as User;
- await client.database.history.create("unban", interaction.guild.id, member, interaction.user);
+ await client.database.history.create("unban", interaction.guild.id, member, interaction.user, "No reason provided");
const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger;
const data = {
meta: {
@@ -64,7 +64,7 @@
name: entry(member.id, renderUser(member)),
unbanned: entry(new Date().getTime(), renderDelta(new Date().getTime())),
unbannedBy: entry(interaction.user.id, renderUser(interaction.user)),
- accountCreated: entry(member.createdAt, renderDelta(member.createdAt))
+ accountCreated: entry(member.createdTimestamp, renderDelta(member.createdTimestamp))
},
hidden: {
guild: interaction.guild.id
@@ -110,13 +110,13 @@
const check = (interaction: CommandInteraction) => {
if (!interaction.guild) return;
const member = interaction.member as GuildMember;
- const me = interaction.guild.me!;
+ const me = interaction.guild.members.me!;
// Check if Nucleus can unban members
- if (!me.permissions.has("BAN_MEMBERS")) throw new Error("I do not have the *Ban Members* permission");
+ if (!me.permissions.has("BanMembers")) return "I do not have the *Ban Members* permission";
// Allow the owner to unban anyone
if (member.id === interaction.guild.ownerId) return true;
// Check if the user has ban_members permission
- if (!member.permissions.has("BAN_MEMBERS")) throw new Error("You do not have the *Ban Members* permission");
+ if (!member.permissions.has("BanMembers")) return "You do not have the *Ban Members* permission";
// Allow unban
return true;
};
diff --git a/src/commands/mod/unmute.ts b/src/commands/mod/unmute.ts
index c93f8cc..e2585e1 100644
--- a/src/commands/mod/unmute.ts
+++ b/src/commands/mod/unmute.ts
@@ -43,6 +43,7 @@
notify
)
.addReasonButton(reason ?? "")
+ .setFailedMessage("No changes were made", "Success", "PUNISH.MUTE.GREEN")
.send(reason !== null);
if (confirmation.cancelled) timedOut = true;
else if (confirmation.success !== undefined) success = true;
@@ -51,96 +52,83 @@
notify = confirmation.components!["notify"]!.active;
}
} while (!timedOut && !success);
- if (confirmation.cancelled) return;
- if (confirmation.success) {
- let dmSent = false;
- let dmMessage;
- try {
- if (notify) {
- dmMessage = await (interaction.options.getMember("user") as GuildMember).send({
- embeds: [
- new EmojiEmbed()
- .setEmoji("PUNISH.MUTE.GREEN")
- .setTitle("Unmuted")
- .setDescription(
- `You have been unmuted in ${interaction.guild.name}` +
- (reason ? ` for:\n> ${reason}` : " with no reason provided.")
- )
- .setStatus("Success")
- ]
- });
- dmSent = true;
- }
- } catch {
- dmSent = false;
- }
- const member = interaction.options.getMember("user") as GuildMember;
- try {
- member.timeout(0, reason ?? "*No reason provided*");
- } catch {
- await interaction.editReply({
+ if (confirmation.cancelled || !confirmation.success) return;
+ let dmSent = false;
+ let dmMessage;
+ try {
+ if (notify) {
+ dmMessage = await (interaction.options.getMember("user") as GuildMember).send({
embeds: [
new EmojiEmbed()
- .setEmoji("PUNISH.MUTE.RED")
- .setTitle("Unmute")
- .setDescription("Something went wrong and the user was not unmuted")
- .setStatus("Danger")
- ],
- components: []
+ .setEmoji("PUNISH.MUTE.GREEN")
+ .setTitle("Unmuted")
+ .setDescription(
+ `You have been unmuted in ${interaction.guild.name}` +
+ (reason ? ` for:\n> ${reason}` : " with no reason provided.")
+ )
+ .setStatus("Success")
+ ]
});
- if (dmSent && dmMessage) await dmMessage.delete();
- return;
+ dmSent = true;
}
- await client.database.history.create(
- "unmute",
- interaction.guild.id,
- (interaction.options.getMember("user") as GuildMember).user,
- interaction.user,
- reason
- );
- const data = {
- meta: {
- type: "memberUnmute",
- displayName: "Unmuted",
- calculateType: "guildMemberPunish",
- color: NucleusColors.green,
- emoji: "PUNISH.MUTE.GREEN",
- timestamp: new Date().getTime()
- },
- list: {
- memberId: entry(member.user.id, `\`${member.user.id}\``),
- name: entry(member.user.id, renderUser(member.user)),
- unmuted: entry(new Date().getTime().toString(), renderDelta(new Date().getTime())),
- unmutedBy: entry(interaction.user.id, renderUser(interaction.user))
- },
- hidden: {
- guild: interaction.guild.id
- }
- };
- log(data);
- const failed = !dmSent && notify;
- await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setEmoji(`PUNISH.MUTE.${failed ? "YELLOW" : "GREEN"}`)
- .setTitle("Unmute")
- .setDescription("The member was unmuted" + (failed ? ", but could not be notified" : ""))
- .setStatus(failed ? "Warning" : "Success")
- ],
- components: []
- });
- } else {
- await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setEmoji("PUNISH.MUTE.GREEN")
- .setTitle("Unmute")
- .setDescription("No changes were made")
- .setStatus("Success")
- ],
- components: []
- });
+ } catch {
+ dmSent = false;
}
+ const member = interaction.options.getMember("user") as GuildMember;
+ try {
+ member.timeout(0, reason ?? "*No reason provided*");
+ } catch {
+ await interaction.editReply({
+ embeds: [
+ new EmojiEmbed()
+ .setEmoji("PUNISH.MUTE.RED")
+ .setTitle("Unmute")
+ .setDescription("Something went wrong and the user was not unmuted")
+ .setStatus("Danger")
+ ],
+ components: []
+ });
+ if (dmSent && dmMessage) await dmMessage.delete();
+ return;
+ }
+ await client.database.history.create(
+ "unmute",
+ interaction.guild.id,
+ (interaction.options.getMember("user") as GuildMember).user,
+ interaction.user,
+ reason
+ );
+ const data = {
+ meta: {
+ type: "memberUnmute",
+ displayName: "Unmuted",
+ calculateType: "guildMemberPunish",
+ color: NucleusColors.green,
+ emoji: "PUNISH.MUTE.GREEN",
+ timestamp: new Date().getTime()
+ },
+ list: {
+ memberId: entry(member.user.id, `\`${member.user.id}\``),
+ name: entry(member.user.id, renderUser(member.user)),
+ unmuted: entry(new Date().getTime().toString(), renderDelta(new Date().getTime())),
+ unmutedBy: entry(interaction.user.id, renderUser(interaction.user))
+ },
+ hidden: {
+ guild: interaction.guild.id
+ }
+ };
+ log(data);
+ const failed = !dmSent && notify;
+ await interaction.editReply({
+ embeds: [
+ new EmojiEmbed()
+ .setEmoji(`PUNISH.MUTE.${failed ? "YELLOW" : "GREEN"}`)
+ .setTitle("Unmute")
+ .setDescription("The member was unmuted" + (failed ? ", but could not be notified" : ""))
+ .setStatus(failed ? "Warning" : "Success")
+ ],
+ components: []
+ });
};
const check = (interaction: CommandInteraction) => {
@@ -152,18 +140,18 @@
const mePos = me.roles.cache.size > 1 ? me.roles.highest.position : 0;
const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0;
// Do not allow unmuting the owner
- if (member.id === interaction.guild.ownerId) throw new Error("You cannot unmute the owner of the server");
+ if (member.id === interaction.guild.ownerId) return "You cannot unmute the owner of the server";
// Check if Nucleus can unmute the member
- if (!(mePos > applyPos)) throw new Error("I do not have a role higher than that member");
+ if (!(mePos > applyPos)) return "I do not have a role higher than that member";
// Check if Nucleus has permission to unmute
- if (!me.permissions.has("ModerateMembers")) throw new Error("I do not have the *Moderate Members* permission");
+ if (!me.permissions.has("ModerateMembers")) return "I do not have the *Moderate Members* permission";
// Allow the owner to unmute anyone
if (member.id === interaction.guild.ownerId) return true;
// Check if the user has moderate_members permission
if (!member.permissions.has("ModerateMembers"))
- throw new Error("You do not have the *Moderate Members* permission");
+ return "You do not have the *Moderate Members* permission";
// Check if the user is below on the role list
- if (!(memberPos > applyPos)) throw new Error("You do not have a role higher than that member");
+ if (!(memberPos > applyPos)) return "You do not have a role higher than that member";
// Allow unmute
return true;
};
diff --git a/src/commands/mod/viewas.ts b/src/commands/mod/viewas.ts
index 8b2864a..6216a37 100644
--- a/src/commands/mod/viewas.ts
+++ b/src/commands/mod/viewas.ts
@@ -1,10 +1,13 @@
+import { LoadingEmbed } from './../../utils/defaults.js';
import Discord, {
CommandInteraction,
GuildMember,
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
- NonThreadGuildBasedChannel
+ NonThreadGuildBasedChannel,
+ StringSelectMenuOptionBuilder,
+ StringSelectMenuBuilder
} from "discord.js";
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import type { GuildBasedChannel } from "discord.js";
@@ -26,183 +29,155 @@
"null": channel[]
}
*/
+ const m = await interaction.reply({embeds: LoadingEmbed, ephemeral: true, fetchReply: true})
- const channels: Record<string, GuildBasedChannel[]> = {"": [] as GuildBasedChannel[]};
+ let channels: Record<string, GuildBasedChannel[]> = {"": []};
- interaction.guild!.channels.fetch().then(channelCollection => {
- channelCollection.forEach(channel => {
- if (!channel) return; // if no channel
- if (channel.type === Discord.ChannelType.GuildCategory) {
- if(!channels[channel!.id]) channels[channel!.id] = [channel];
- } else if (channel.parent) {
- if (!channels[channel.parent.id]) channels[channel.parent.id] = [channel];
- else (channels[channel.parent.id as string])!.push(channel);
- } else {
- channels[""]!.push(channel);
- }
- });
+ const channelCollection = await interaction.guild!.channels.fetch();
+
+ channelCollection.forEach(channel => {
+ if (!channel) return; // if no channel
+ if (channel.type === Discord.ChannelType.GuildCategory) {
+ if(!channels[channel!.id]) channels[channel!.id] = [];
+ } else if (channel.parent) {
+ if (!channels[channel.parent.id]) channels[channel.parent.id] = [channel];
+ else (channels[channel.parent.id as string])!.push(channel);
+ } else {
+ channels[""]!.push(channel);
+ }
});
const member = interaction.options.getMember("member") as Discord.GuildMember;
const autoSortBelow = [Discord.ChannelType.GuildVoice, Discord.ChannelType.GuildStageVoice];
- // for each category, sort its channels. This should be based on the order of the channels, with voice and stage channels sorted below text
- channels = Object.values(channels).map((c) => {
- return c.sort((a: GuildBasedChannel, b: GuildBasedChannel) => {
- if (a.type === Discord.ChannelType.PrivateThread || b.type === Discord.ChannelType.PrivateThread)
- if (autoSortBelow.includes(a.type) && autoSortBelow.includes(b.type)) return a.position ?? 0 - b.position ;
+
+ for (const category in channels) {
+ channels[category] = channels[category]!.sort((a: GuildBasedChannel, b: GuildBasedChannel) => {
+ const disallowedTypes = [Discord.ChannelType.PublicThread, Discord.ChannelType.PrivateThread, Discord.ChannelType.AnnouncementThread];
+ if (disallowedTypes.includes(a.type) || disallowedTypes.includes(b.type)) return 0;
+ a = a as NonThreadGuildBasedChannel;
+ b = b as NonThreadGuildBasedChannel;
+ if (autoSortBelow.includes(a.type) && autoSortBelow.includes(b.type)) return a.position - b.position;
if (autoSortBelow.includes(a.type)) return 1;
if (autoSortBelow.includes(b.type)) return -1;
- return a - b;
+ return a.position - b.position;
});
}
-
+ for (const category in channels) {
+ channels[category] = channels[category]!.filter((c) => {
+ return c.permissionsFor(member).has("ViewChannel");
+ });
+ }
+ for (const category in channels) {
+ channels[category] = channels[category]!.filter((c) => {
+ return !(c.type === Discord.ChannelType.PublicThread || c.type === Discord.ChannelType.PrivateThread || c.type === Discord.ChannelType.AnnouncementThread)
+ });
+ }
+ channels = Object.fromEntries(Object.entries(channels).filter(([_, v]) => v.length > 0));
+ let page = 0;
+ let closed = false;
+ const categoryIDs = Object.keys(channels);
+ const categoryNames = Object.values(channels).map((c) => {
+ return c[0]!.parent?.name ?? "Uncategorised";
+ });
+ // Split the category names into the first and last 25, ignoring the last 25 if there are 25 or less
+ const first25 = categoryNames.slice(0, 25);
+ const last25 = categoryNames.slice(25);
+ const categoryNames25: string[][] = [first25];
+ if (last25.length > 0) categoryNames25.push(last25);
- //OLD CODE START
- // const unprocessedChannels: GuildBasedChannel[] = [];
- // let m;
- // interaction.guild!.channels.cache.forEach((channel) => {
- // if (!channel.parent && channel.type !== Discord.ChannelType.GuildCategory) unprocessedChannels.push(channel);
- // });
- // let channels: GuildBasedChannel[][] = [unprocessedChannels];
- // channels = channels.concat(
- // interaction.guild!.channels.cache
- // .filter((c) => c.type === Discord.ChannelType.GuildCategory)
- // .map((c) => (c as CategoryChannel).children.map((c) => c))
- // );
- // const autoSortBelow = ["GUILD_VOICE", "GUILD_STAGE_VOICE"];
- // channels = channels.map((c) =>
- // c.sort((a, b) => {
- // if (autoSortBelow.includes(a.type) && autoSortBelow.includes(b.type)) return a.position - b.position;
- // if (autoSortBelow.includes(a.type)) return 1;
- // if (autoSortBelow.includes(b.type)) return -1;
- // return a.position - b.position;
- // })
- // );
- // // Sort all arrays by the position of the first channels parent position
- // channels = channels.sort((a, b) => {
- // if (!a[0].parent) return -1;
- // if (!b[0].parent) return 1;
- // return a[0].parent.position - b[0].parent.position;
- // });
- // const member = interaction.options.getMember("member") as Discord.GuildMember;
- // m = await interaction.reply({
- // embeds: [
- // new EmojiEmbed()
- // .setEmoji("MEMBER.JOIN")
- // .setTitle("Viewing as " + member.displayName)
- // .setStatus("Success")
- // ],
- // ephemeral: true,
- // fetchReply: true
- // });
- // let page = 0;
- // let timedOut = false;
- // while (!timedOut) {
- // m = await interaction.editReply({
- // embeds: [
- // new EmojiEmbed()
- // .setEmoji("MEMBER.JOIN")
- // .setTitle("Viewing as " + member.displayName)
- // .setStatus("Success")
- // .setDescription(
- // `**${channels[page][0].parent ? channels[page][0].parent.name : "Uncategorised"}**` +
- // "\n" +
- // channels[page]
- // .map((c) => {
- // let channelType = c.type;
- // if (interaction.guild.rulesChannelId === c.id) channelType = "RULES";
- // else if ("nsfw" in c && c.nsfw) channelType += "_NSFW";
- // return c.permissionsFor(member).has("VIEW_CHANNEL")
- // ? `${getEmojiByName("ICONS.CHANNEL." + channelType)} ${c.name}\n` +
- // (() => {
- // if ("threads" in c && c.threads.cache.size > 0) {
- // return (
- // c.threads.cache
- // .map(
- // (t) =>
- // ` ${
- // getEmojiByName("ICONS.CHANNEL.THREAD_PIPE") +
- // " " +
- // getEmojiByName("ICONS.CHANNEL.THREAD_CHANNEL")
- // } ${t.name}`
- // )
- // .join("\n") + "\n"
- // );
- // }
- // return "";
- // })()
- // : "";
- // })
- // .join("") +
- // "\n" +
- // pageIndicator(channels.length, page)
- // )
- // ],
- // components: [
- // new ActionRowBuilder().addComponents([
- // new SelectMenuBuilder()
- // .setOptions(
- // channels.map((c, index) => ({
- // label: c[0].parent ? c[0].parent.name : "Uncategorised",
- // value: index.toString(),
- // default: page === index
- // }))
- // )
- // .setCustomId("select")
- // .setMaxValues(1)
- // .setMinValues(1)
- // .setPlaceholder("Select a category")
- // ]),
- // new ActionRowBuilder().addComponents([
- // new ButtonBuilder()
- // .setLabel(
- // page === 0
- // ? ""
- // : channels[page - 1][0].parent
- // ? channels[page - 1][0].parent.name
- // : "Uncategorised"
- // )
- // .setDisabled(page === 0)
- // .setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
- // .setStyle(ButtonStyle.Primary)
- // .setCustomId("previous"),
- // new ButtonBuilder()
- // .setLabel(
- // page === channels.length - 1
- // ? ""
- // : channels[page + 1][0].parent
- // ? channels[page + 1][0].parent.name
- // : "Uncategorised"
- // )
- // .setDisabled(page === channels.length - 1)
- // .setEmoji(getEmojiByName("CONTROL.RIGHT", "id"))
- // .setStyle(ButtonStyle.Primary)
- // .setCustomId("next")
- // ])
- // ]
- // });
- // let i;
- // try {
- // i = await m.awaitMessageComponent({ time: 300000 });
- // } catch (e) {
- // timedOut = true;
- // continue;
- // }
- // i.deferUpdate();
- // if (i.customId === "next") {
- // page++;
- // } else if (i.customId === "previous") {
- // page--;
- // } else if (i.customId === "select") {
- // page = parseInt(i.values[0]);
- // }
- // }
-
+ const channelTypeEmoji: Record<number, string> = {
+ 0: "GUILD_TEXT", // Text channel
+ 2: "GUILD_VOICE", // Voice channel
+ 5: "GUILD_NEWS", // Announcement channel
+ 13: "GUILD_STAGE_VOICE", // Stage channel
+ 15: "FORUM", // Forum channel
+ 99: "RULES" // Rules channel
+ };
+ const NSFWAvailable: number[] = [0, 2, 5, 13];
+ const rulesChannel = interaction.guild!.rulesChannel?.id;
+
+ async function nameFromChannel(channel: GuildBasedChannel): Promise<string> {
+ let channelType = channel.type;
+ if (channelType === Discord.ChannelType.GuildCategory) return "";
+ if (channel.id === rulesChannel) channelType = 99
+ let threads: Discord.ThreadChannel[] = [];
+ if ("threads" in channel) {
+ threads = channel.threads.cache.toJSON().map((t) => t as Discord.ThreadChannel);
+ }
+ const nsfw = ("nsfw" in channel ? channel.nsfw : false) && NSFWAvailable.includes(channelType)
+ const emojiName = channelTypeEmoji[channelType] + (nsfw ? "_NSFW" : "");
+ const emoji = getEmojiByName("ICONS.CHANNEL." + (threads.length ? "THREAD_CHANNEL" : emojiName));
+ let current = `${emoji} ${channel.name}`;
+ if (threads.length) {
+ for (const thread of threads) {
+ current += `\n${getEmojiByName("ICONS.CHANNEL.THREAD_PIPE")} ${thread.name}`;
+ }
+ }
+ return current;
+ }
+
+ while (!closed) {
+ const category = categoryIDs[page]!;
+ let description = "";
+ for (const channel of channels[category]!) {
+ description += `${await nameFromChannel(channel)}\n`;
+ }
+
+ const parsedCategorySelectMenu: ActionRowBuilder<StringSelectMenuBuilder | ButtonBuilder>[] = categoryNames25.map(
+ (categories, set) => { return new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(new StringSelectMenuBuilder()
+ .setCustomId("category")
+ .setMinValues(1)
+ .setMaxValues(1)
+ .setOptions(categories.map((c, i) => {
+ return new StringSelectMenuOptionBuilder()
+ .setLabel(c)
+ .setValue((set * 25 + i).toString())
+ // @ts-expect-error
+ .setEmoji(getEmojiByName("ICONS.CHANNEL.CATEGORY", "id")) // Again, this is valid but TS doesn't think so
+ .setDefault((set * 25 + i) === page)
+ }))
+ )}
+ );
+
+ const components: ActionRowBuilder<ButtonBuilder | StringSelectMenuBuilder>[] = parsedCategorySelectMenu
+ components.push(new ActionRowBuilder<ButtonBuilder>().addComponents(
+ new ButtonBuilder()
+ .setCustomId("back")
+ .setStyle(ButtonStyle.Secondary)
+ .setDisabled(page === 0)
+ .setEmoji(getEmojiByName("CONTROL.LEFT", "id")),
+ new ButtonBuilder()
+ .setCustomId("right")
+ .setStyle(ButtonStyle.Secondary)
+ .setDisabled(page === categoryIDs.length - 1)
+ .setEmoji(getEmojiByName("CONTROL.RIGHT", "id"))
+ ));
+
+ await interaction.editReply({
+ embeds: [new EmojiEmbed()
+ .setEmoji("MEMBER.JOIN")
+ .setTitle("Viewing as " + member.displayName)
+ .setStatus("Success")
+ .setDescription(description + "\n" + pageIndicator(categoryIDs.length, page))
+ ], components: components
+ });
+ let i;
+ try {
+ i = await m.awaitMessageComponent({filter: (i) => i.user.id === interaction.user.id, time: 30000});
+ } catch (e) {
+ closed = true;
+ continue;
+ }
+ i.deferUpdate();
+ if (i.customId === "back") page--;
+ else if (i.customId === "right") page++;
+ else if (i.customId === "category" && i.isStringSelectMenu()) page = parseInt(i.values[0]!);
+ }
};
const check = (interaction: CommandInteraction) => {
const member = interaction.member as GuildMember;
- if (!member.permissions.has("MANAGE_ROLES")) throw new Error("You do not have the *Manage Roles* permission");
+ if (!member.permissions.has("ManageRoles")) return "You do not have the *Manage Roles* permission";
return true;
};
diff --git a/src/commands/mod/warn.ts b/src/commands/mod/warn.ts
index c4aa7c3..93241e1 100644
--- a/src/commands/mod/warn.ts
+++ b/src/commands/mod/warn.ts
@@ -5,7 +5,7 @@
import keyValueList from "../../utils/generateKeyValueList.js";
import { create, areTicketsEnabled } from "../../actions/createModActionTicket.js";
import client from "../../utils/client.js";
-import { LinkWarningFooter } from "../../utils/defaultEmbeds.js";
+import { LinkWarningFooter } from "../../utils/defaults.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder
@@ -186,7 +186,7 @@
let component;
try {
component = await m.awaitMessageComponent({
- filter: (m) => m.user.id === interaction.user.id,
+ filter: (m) => m.user.id === interaction.user.id && m.channel!.id === interaction.channel!.id,
time: 300000
});
} catch (e) {
@@ -279,18 +279,18 @@
if (!interaction.guild) return;
const member = interaction.member as GuildMember;
const apply = interaction.options.getMember("user") as GuildMember | null;
- if (apply === null) throw new Error("That member is not in the server");
+ if (apply === null) return "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");
+ if (member.user.bot) return "I cannot warn bots";
// Allow the owner to warn anyone
if (member.id === interaction.guild.ownerId) return true;
// Check if the user has moderate_members permission
if (!member.permissions.has("ModerateMembers"))
- throw new Error("You do not have the *Moderate Members* permission");
+ return "You do not have the *Moderate Members* permission";
// Check if the user is below on the role list
- if (!(memberPos > applyPos)) throw new Error("You do not have a role higher than that member");
+ if (!(memberPos > applyPos)) return "You do not have a role higher than that member";
// Allow warn
return true;
};
diff --git a/src/commands/nucleus/ping.ts b/src/commands/nucleus/ping.ts
index 3cbd049..12f1c6b 100644
--- a/src/commands/nucleus/ping.ts
+++ b/src/commands/nucleus/ping.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../../utils/defaults.js";
import type { CommandInteraction } from "discord.js";
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
diff --git a/src/commands/nucleus/premium.ts b/src/commands/nucleus/premium.ts
index 9bbc36e..745f167 100644
--- a/src/commands/nucleus/premium.ts
+++ b/src/commands/nucleus/premium.ts
@@ -1,5 +1,5 @@
-import { CommandInteraction } from "discord.js";
-import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { CommandInteraction } from "discord.js";
+import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
diff --git a/src/commands/nucleus/suggest.ts b/src/commands/nucleus/suggest.ts
index e31696b..de0e69b 100644
--- a/src/commands/nucleus/suggest.ts
+++ b/src/commands/nucleus/suggest.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from './../../utils/defaultEmbeds.js';
+import { LoadingEmbed } from '../../utils/defaults.js';
import { ButtonStyle, CommandInteraction } from "discord.js";
import Discord from "discord.js";
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
@@ -29,56 +29,44 @@
)
.setColor("Danger")
.setInverted(true)
+ .setFailedMessage("Your suggestion was deleted", "Success", "ICONS.ADD")
.send(true);
- if (confirmation.cancelled) return;
- if (confirmation.success) {
- await (client.channels.cache.get("955161206459600976") as Discord.TextChannel).send({
- embeds: [
- new EmojiEmbed()
- .setTitle("Suggestion")
- .setDescription(
- `**From:** ${renderUser(interaction.member!.user as Discord.User)}\n**Suggestion:**\n> ${suggestion}\n\n` +
- `**Server:** ${interaction.guild!.name} (${interaction.guild!.id})\n`,
- )
- .setStatus("Warning")
- ], components: [new Discord.ActionRowBuilder<Discord.ButtonBuilder>().addComponents(
- new Discord.ButtonBuilder()
- .setCustomId("suggestionAccept")
- .setLabel("Accept")
- .setStyle(ButtonStyle.Secondary)
- .setEmoji(getEmojiByName("ICONS.ADD", "id")),
- new Discord.ButtonBuilder()
- .setCustomId("suggestionDeny")
- .setLabel("Delete")
- .setStyle(ButtonStyle.Secondary)
- .setEmoji(getEmojiByName("ICONS.REMOVE", "id"))
- )]
- });
- await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setEmoji("ICONS.ADD")
- .setTitle("Suggest")
- .setDescription("Your suggestion was sent successfully")
- .setStatus("Success")
- ],
- components: []
- });
- } else {
- await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setEmoji("ICONS.OPP.ADD")
- .setTitle("Suggest")
- .setDescription("No changes were made")
- .setStatus("Danger")
- ],
- components: []
- });
- }
+ if (confirmation.cancelled || !confirmation.success) return;
+ await (client.channels.cache.get("955161206459600976") as Discord.TextChannel).send({
+ embeds: [
+ new EmojiEmbed()
+ .setTitle("Suggestion")
+ .setDescription(
+ `**From:** ${renderUser(interaction.member!.user as Discord.User)}\n**Suggestion:**\n> ${suggestion}\n\n` +
+ `**Server:** ${interaction.guild!.name} (${interaction.guild!.id})\n`,
+ )
+ .setStatus("Warning")
+ ], components: [new Discord.ActionRowBuilder<Discord.ButtonBuilder>().addComponents(
+ new Discord.ButtonBuilder()
+ .setCustomId("suggestionAccept")
+ .setLabel("Accept")
+ .setStyle(ButtonStyle.Secondary)
+ .setEmoji(getEmojiByName("ICONS.ADD", "id")),
+ new Discord.ButtonBuilder()
+ .setCustomId("suggestionDeny")
+ .setLabel("Delete")
+ .setStyle(ButtonStyle.Secondary)
+ .setEmoji(getEmojiByName("ICONS.REMOVE", "id"))
+ )]
+ });
+ await interaction.editReply({
+ embeds: [
+ new EmojiEmbed()
+ .setEmoji("ICONS.ADD")
+ .setTitle("Suggest")
+ .setDescription("Your suggestion was sent successfully")
+ .setStatus("Success")
+ ],
+ components: []
+ });
};
-const check = (_interaction: CommandInteraction) => {
+const check = () => {
return true;
};
diff --git a/src/commands/privacy.ts b/src/commands/privacy.ts
index a427688..9100302 100644
--- a/src/commands/privacy.ts
+++ b/src/commands/privacy.ts
@@ -1,43 +1,17 @@
-import { LoadingEmbed } from "./../utils/defaultEmbeds.js";
-import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
+import { LoadingEmbed } from "../utils/defaults.js";
+import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle, StringSelectMenuOptionBuilder, SelectMenuOptionBuilder, StringSelectMenuBuilder } from "discord.js";
import { SlashCommandBuilder } from "@discordjs/builders";
import EmojiEmbed from "../utils/generateEmojiEmbed.js";
import getEmojiByName from "../utils/getEmojiByName.js";
import createPageIndicator from "../utils/createPageIndicator.js";
import client from "../utils/client.js";
import confirmationMessage from "../utils/confirmationMessage.js";
+import { Embed } from "../utils/defaults.js";
const command = new SlashCommandBuilder()
.setName("privacy")
.setDescription("Information and options for you and your server's settings");
-class Embed {
- embed: Discord.EmbedBuilder;
- title: string;
- description = "";
- pageId = 0;
- components?: ActionRowBuilder[] = [];
- setEmbed(embed: Discord.EmbedBuilder) {
- 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;
- }
- setComponents(components: ActionRowBuilder[]) {
- this.components = components;
- return this;
- }
-}
const callback = async (interaction: CommandInteraction): Promise<void> => {
const pages = [
@@ -78,7 +52,7 @@
.setDescription(
"**Facebook** - Facebook trackers include data such as your date of birth, and guess your age if not entered, your preferences, who you interact with and more.\n" +
"**AMP** - AMP is a technology that allows websites to be served by Google. This means Google can store and track data, and are pushing this to as many pages as possible.\n\n" +
- "Transcripts allow you to store all messages sent in a channel. This could be an issue in some cases, as they are hosted on [Pastebin](https://pastebin.com), so a leaked link could show all messages sent in the channel.\n"
+ "Transcripts allow you to store all messages sent in a channel. This could be an issue in some cases, as they are hosted on [Pastebin](https://pastebin.com), so a leaked link could show all messages sent in the channel.\n" // TODO: Not on pastebin
)
.setEmoji("NUCLEUS.LOGO")
.setStatus("Danger")
@@ -87,7 +61,7 @@
.setDescription("Regarding Facebook and AMP filter types, and ticket transcripts")
.setPageId(2)
].concat(
- (interaction.member as Discord.GuildMember).permissions.has("ADMINISTRATOR")
+ (interaction.member as Discord.GuildMember).permissions.has("Administrator")
? [
new Embed()
.setEmbed(
@@ -101,7 +75,7 @@
.setDescription("Options")
.setPageId(3)
.setComponents([
- new ActionRowBuilder().addComponents([
+ new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setLabel("Clear all data")
.setCustomId("clear-all-data")
@@ -116,20 +90,20 @@
fetchReply: true,
ephemeral: true
});
- let page = 0;
+ let page = parseInt(client.preloadPage[interaction.channel!.id] ? client.preloadPage[interaction.channel!.id]?.argument! : "0");
let selectPaneOpen = false;
let nextFooter = null;
let timedOut = false;
while (!timedOut) {
- let selectPane = [];
+ let selectPane: Discord.ActionRowBuilder<ButtonBuilder | StringSelectMenuBuilder>[] = [];
if (selectPaneOpen) {
- const options = [];
+ const options: SelectMenuOptionBuilder[] = [];
pages.forEach((embed) => {
options.push(
- new SelectMenuOption({
+ new StringSelectMenuOptionBuilder({
label: embed.title,
value: embed.pageId.toString(),
description: embed.description || ""
@@ -137,8 +111,8 @@
);
});
selectPane = [
- new ActionRowBuilder().addComponents([
- new Discord.SelectMenuBuilder()
+ new ActionRowBuilder<StringSelectMenuBuilder>().addComponents([
+ new Discord.StringSelectMenuBuilder()
.addOptions(options)
.setCustomId("page")
.setMaxValues(1)
@@ -146,8 +120,8 @@
])
];
}
- const components = selectPane.concat([
- new ActionRowBuilder().addComponents([
+ const components: ActionRowBuilder<ButtonBuilder | StringSelectMenuBuilder>[] = selectPane.concat([
+ new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setCustomId("left")
.setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
@@ -163,36 +137,39 @@
.setEmoji(getEmojiByName("CONTROL.RIGHT", "id"))
.setStyle(ButtonStyle.Secondary)
.setDisabled(page === pages.length - 1)
- ])
+ )
]);
- const em = new Discord.EmbedBuilder(pages[page].embed);
- em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page));
- em.setFooter({ text: nextFooter ?? "" });
+ const em = new Discord.EmbedBuilder(pages[page]!.embed);
+ em.setDescription(em.data.description + "\n\n" + createPageIndicator(pages.length, page));
+ if (nextFooter) em.setFooter({ text: nextFooter });
await interaction.editReply({
embeds: [em],
- components: components.concat(pages[page].components)
+ components: components.concat(pages[page]?.componentsToSet ?? [])
});
let i;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
continue;
}
nextFooter = null;
i.deferUpdate();
- if (i.component.customId === "left") {
+ if (i.customId === "left") {
if (page > 0) page--;
selectPaneOpen = false;
- } else if (i.component.customId === "right") {
+ } else if (i.customId === "right") {
if (page < pages.length - 1) page++;
selectPaneOpen = false;
- } else if (i.component.customId === "select") {
+ } else if (i.customId === "select") {
selectPaneOpen = !selectPaneOpen;
- } else if (i.component.customId === "page") {
- page = parseInt(i.values[0]);
+ } else if (i.customId === "page" && i.isStringSelectMenu()) {
+ page = parseInt(i.values[0]!);
selectPaneOpen = false;
- } else if (i.component.customId === "clear-all-data") {
+ } else if (i.customId === "clear-all-data") {
const confirmation = await new confirmationMessage(interaction)
.setEmoji("CONTROL.BLOCKCROSS")
.setTitle("Clear All Data")
@@ -206,8 +183,8 @@
break;
}
if (confirmation.success) {
- client.database.guilds.delete(interaction.guild.id);
- client.database.history.delete(interaction.guild.id);
+ client.database.guilds.delete(interaction.guild!.id);
+ client.database.history.delete(interaction.guild!.id);
nextFooter = "All data cleared";
continue;
} else {
@@ -215,15 +192,15 @@
continue;
}
} else {
- const em = new Discord.EmbedBuilder(pages[page].embed);
- em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page));
+ const em = new Discord.EmbedBuilder(pages[page]!.embed);
+ em.setDescription(em.data.description + "\n\n" + createPageIndicator(pages.length, page));
em.setFooter({ text: "Message closed" });
interaction.editReply({ embeds: [em], components: [] });
return;
}
}
- const em = new Discord.EmbedBuilder(pages[page].embed);
- em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page));
+ const em = new Discord.EmbedBuilder(pages[page]!.embed);
+ em.setDescription(em.data.description + "\n\n" + createPageIndicator(pages.length, page));
em.setFooter({ text: "Message timed out" });
await interaction.editReply({
embeds: [em],
@@ -231,7 +208,7 @@
});
};
-const check = (_interaction: CommandInteraction) => {
+const check = () => {
return true;
};
diff --git a/src/commands/role/user.ts b/src/commands/role/user.ts
index ac94b47..ad29811 100644
--- a/src/commands/role/user.ts
+++ b/src/commands/role/user.ts
@@ -1,5 +1,5 @@
-import { CommandInteraction, GuildMember, Role } from "discord.js";
-import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { CommandInteraction, GuildMember, Role, User } from "discord.js";
+import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import client from "../../utils/client.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
import keyValueList from "../../utils/generateKeyValueList.js";
@@ -28,79 +28,68 @@
const callback = async (interaction: CommandInteraction): Promise<unknown> => {
const { renderUser, renderRole } = client.logger;
- const action = interaction.options.getString("action");
+ const action = interaction.options.get("action")?.value as string;
+ const role: Role = (await interaction.guild!.roles.fetch(interaction.options.get("role")?.value as string))!;
// TODO:[Modals] Replace this with a modal
const confirmation = await new confirmationMessage(interaction)
.setEmoji("GUILD.ROLES.DELETE")
.setTitle("Role")
.setDescription(
keyValueList({
- user: renderUser(interaction.options.getUser("user")),
- role: renderRole(interaction.options.get("role"))
+ user: renderUser(interaction.options.getUser("user")! as User),
+ role: renderRole(role)
}) +
`\nAre you sure you want to ${
action === "give" ? "give the role to" : "remove the role from"
} ${interaction.options.getUser("user")}?`
)
.setColor("Danger")
+ .setFailedMessage("No changes were made", "Success", "GUILD.ROLES.CREATE")
.send();
- if (confirmation.cancelled) return;
- if (confirmation.success) {
- try {
- const member = interaction.options.getMember("user") as GuildMember;
- const role = interaction.options.get("role") as unknown as Role;
- if (interaction.options.getString("action") === "give") {
- member.roles.add(role);
- } else {
- member.roles.remove(role);
- }
- } catch (e) {
- return await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setTitle("Role")
- .setDescription("Something went wrong and the role could not be added")
- .setStatus("Danger")
- .setEmoji("CONTROL.BLOCKCROSS")
- ],
- components: []
- });
+ if (confirmation.cancelled || !confirmation.success) return;
+ try {
+ const member = interaction.options.getMember("user") as GuildMember;
+ if ((interaction.options.get("action")?.value as string) === "give") {
+ member.roles.add(role);
+ } else {
+ member.roles.remove(role);
}
+ } catch (e) {
return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle("Role")
- .setDescription(`The role has been ${action === "give" ? "given" : "removed"} successfully`)
- .setStatus("Success")
- .setEmoji("GUILD.ROLES.CREATE")
- ],
- components: []
- });
- } else {
- await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setEmoji("GUILD.ROLES.CREATE")
- .setTitle("Role")
- .setDescription("No changes were made.")
+ .setDescription("Something went wrong and the role could not be added")
.setStatus("Danger")
+ .setEmoji("CONTROL.BLOCKCROSS")
],
components: []
});
}
+ return await interaction.editReply({
+ embeds: [
+ new EmojiEmbed()
+ .setTitle("Role")
+ .setDescription(`The role has been ${action === "give" ? "given" : "removed"} successfully`)
+ .setStatus("Success")
+ .setEmoji("GUILD.ROLES.CREATE")
+ ],
+ components: []
+ });
};
const check = (interaction: CommandInteraction) => {
const member = interaction.member as GuildMember;
- const me = interaction.guild.me!;
+ if (!interaction.guild) return
+ const me = interaction.guild.members.me!;
const apply = interaction.options.getMember("user") as GuildMember | null;
- if (apply === null) throw new Error("That member is not in the server");
+ if (apply === null) return "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");
+ if (!me.permissions.has("ManageRoles")) return "I do not have the *Manage Roles* permission";
// Allow the owner to role anyone
if (member.id === interaction.guild.ownerId) return true;
// Check if the user has manage_roles permission
- if (!member.permissions.has("MANAGE_ROLES")) throw new Error("You do not have the *Manage Roles* permission");
+ if (!member.permissions.has("ManageRoles")) return "You do not have the *Manage Roles* permission";
// Allow role
return true;
};
diff --git a/src/commands/rolemenu.ts b/src/commands/rolemenu.ts
index 9aad543..c1ceb2e 100644
--- a/src/commands/rolemenu.ts
+++ b/src/commands/rolemenu.ts
@@ -10,7 +10,7 @@
await roleMenu(interaction);
};
-const check = (_interaction: CommandInteraction) => {
+const check = () => {
return true;
};
diff --git a/src/commands/server/about.ts b/src/commands/server/about.ts
index e5bea60..23a53b7 100644
--- a/src/commands/server/about.ts
+++ b/src/commands/server/about.ts
@@ -74,7 +74,7 @@
});
};
-const check = (_interaction: CommandInteraction) => {
+const check = () => {
return true;
};
diff --git a/src/commands/settings/commands.ts b/src/commands/settings/commands.ts
index 4493f79..34a197b 100644
--- a/src/commands/settings/commands.ts
+++ b/src/commands/settings/commands.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../../utils/defaults.js";
import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, TextInputComponent, Role, ButtonStyle } from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
@@ -108,7 +108,10 @@
});
let i;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
continue;
@@ -209,8 +212,8 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
- if (!member.permissions.has("MANAGE_GUILD"))
- throw new Error("You must have the *Manage Server* permission to use this command");
+ if (!member.permissions.has("ManageGuild"))
+ return "You must have the *Manage Server* permission to use this command";
return true;
};
diff --git a/src/commands/settings/filters.ts b/src/commands/settings/filters.ts
index 1a297ca..f0291b9 100644
--- a/src/commands/settings/filters.ts
+++ b/src/commands/settings/filters.ts
@@ -10,8 +10,8 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
- if (!member.permissions.has("MANAGE_MESSAGES"))
- throw new Error("You must have the *Manage Messages* permission to use this command");
+ if (!member.permissions.has("ManageMessages"))
+ return "You must have the *Manage Messages* permission to use this command";
return true;
};
diff --git a/src/commands/settings/logs/attachment.ts b/src/commands/settings/logs/attachment.ts
index 7d4fef3..326246a 100644
--- a/src/commands/settings/logs/attachment.ts
+++ b/src/commands/settings/logs/attachment.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../../../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../../../utils/defaults.js";
import { ChannelType } from "discord-api-types/v9";
import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
@@ -147,7 +147,10 @@
});
let i;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
continue;
@@ -190,8 +193,8 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
- if (!member.permissions.has("MANAGE_GUILD"))
- throw new Error("You must have the *Manage Server* permission to use this command");
+ if (!member.permissions.has("ManageGuild"))
+ return "You must have the *Manage Server* permission to use this command";
return true;
};
diff --git a/src/commands/settings/logs/channel.ts b/src/commands/settings/logs/channel.ts
index 0288bf7..9129841 100644
--- a/src/commands/settings/logs/channel.ts
+++ b/src/commands/settings/logs/channel.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../../../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../../../utils/defaults.js";
import { ChannelType } from "discord-api-types/v9";
import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
@@ -140,7 +140,10 @@
});
let i;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
}
@@ -182,8 +185,8 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
- if (!member.permissions.has("MANAGE_GUILD"))
- throw new Error("You must have the *Manage Server* permission to use this command");
+ if (!member.permissions.has("ManageGuild"))
+ return "You must have the *Manage Server* permission to use this command";
return true;
};
diff --git a/src/commands/settings/logs/events.ts b/src/commands/settings/logs/events.ts
index 9eaf25c..ae04e89 100644
--- a/src/commands/settings/logs/events.ts
+++ b/src/commands/settings/logs/events.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../../../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../../../utils/defaults.js";
import Discord, { CommandInteraction, Message, ActionRowBuilder, ButtonBuilder, SelectMenuBuilder, ButtonStyle } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
@@ -75,7 +75,10 @@
})) as Message;
let i;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
continue;
@@ -105,8 +108,8 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
- if (!member.permissions.has("MANAGE_GUILD"))
- throw new Error("You must have the *Manage Server* permission to use this command");
+ if (!member.permissions.has("ManageGuild"))
+ return "You must have the *Manage Server* permission to use this command";
return true;
};
diff --git a/src/commands/settings/logs/staff.ts b/src/commands/settings/logs/staff.ts
index c1b2380..5a23839 100644
--- a/src/commands/settings/logs/staff.ts
+++ b/src/commands/settings/logs/staff.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../../../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../../../utils/defaults.js";
import { ChannelType } from "discord-api-types/v9";
import Discord, { CommandInteraction, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
import EmojiEmbed from "../../../utils/generateEmojiEmbed.js";
@@ -144,7 +144,10 @@
});
let i;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
continue;
@@ -187,8 +190,8 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
- if (!member.permissions.has("MANAGE_GUILD"))
- throw new Error("You must have the *Manage Server* permission to use this command");
+ if (!member.permissions.has("ManageGuild"))
+ return "You must have the *Manage Server* permission to use this command";
return true;
};
diff --git a/src/commands/settings/rolemenu.ts b/src/commands/settings/rolemenu.ts
index 02ab93e..e05485b 100644
--- a/src/commands/settings/rolemenu.ts
+++ b/src/commands/settings/rolemenu.ts
@@ -14,8 +14,8 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
- if (!member.permissions.has("MANAGE_ROLES"))
- throw Error("You must have the *Manage Roles* permission to use this command");
+ if (!member.permissions.has("ManageRoles"))
+ return "You must have the *Manage Roles* permission to use this command";
return true;
};
diff --git a/src/commands/settings/stats.ts b/src/commands/settings/stats.ts
index a768cb8..0e4c50b 100644
--- a/src/commands/settings/stats.ts
+++ b/src/commands/settings/stats.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../../utils/defaults.js";
import Discord, { CommandInteraction, Message, ActionRowBuilder, SelectMenuBuilder } from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
@@ -196,7 +196,10 @@
});
let i;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
continue;
@@ -219,8 +222,8 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
- if (!member.permissions.has("MANAGE_CHANNELS"))
- throw new Error("You must have the *Manage Channels* permission to use this command");
+ if (!member.permissions.has("manageChannels"))
+ return "You must have the *Manage Channels* permission to use this command";
return true;
};
@@ -237,3 +240,4 @@
export { command };
export { callback };
export { check };
+export { autocomplete };
\ No newline at end of file
diff --git a/src/commands/settings/tickets.ts b/src/commands/settings/tickets.ts
index 70ba52d..ee0f2d1 100644
--- a/src/commands/settings/tickets.ts
+++ b/src/commands/settings/tickets.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../../utils/defaults.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
@@ -276,7 +276,10 @@
})) as Message;
let i: MessageComponentInteraction;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
continue;
@@ -376,7 +379,10 @@
});
let i: MessageComponentInteraction;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
innerTimedOut = true;
continue;
@@ -627,7 +633,10 @@
}
let i;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
continue;
@@ -734,7 +743,7 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
if (!member.permissions.has("ManageGuild"))
- throw new Error("You must have the *Manage Server* permission to use this command");
+ return "You must have the *Manage Server* permission to use this command";
return true;
};
diff --git a/src/commands/settings/verify.ts b/src/commands/settings/verify.ts
index cceadae..a8b0ae0 100644
--- a/src/commands/settings/verify.ts
+++ b/src/commands/settings/verify.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../../utils/defaults.js";
import Discord, {
CommandInteraction,
Interaction,
@@ -157,7 +157,10 @@
});
let i: MessageComponentInteraction;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
continue;
@@ -243,7 +246,10 @@
});
let i: MessageComponentInteraction;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
innerTimedOut = true;
continue;
@@ -381,7 +387,7 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
if (!member.permissions.has("ManageGuild"))
- throw new Error("You must have the *Manage Server* permission to use this command");
+ return "You must have the *Manage Server* permission to use this command";
return true;
};
diff --git a/src/commands/settings/welcome.ts b/src/commands/settings/welcome.ts
index 7f02cd7..9892638 100644
--- a/src/commands/settings/welcome.ts
+++ b/src/commands/settings/welcome.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../../utils/defaults.js";
import Discord, {
Channel,
CommandInteraction,
@@ -240,7 +240,10 @@
})) as Message;
let i: MessageComponentInteraction;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
continue;
@@ -298,7 +301,7 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
if (!member.permissions.has("ManageGuild"))
- throw new Error("You must have the *Manage Server* permission to use this command");
+ return "You must have the *Manage Server* permission to use this command";
return true;
};
diff --git a/src/commands/tags/create.ts b/src/commands/tags/create.ts
index 5379bf8..788902e 100644
--- a/src/commands/tags/create.ts
+++ b/src/commands/tags/create.ts
@@ -64,7 +64,7 @@
ephemeral: true
});
const confirmation = await new confirmationMessage(interaction)
- .setEmoji("PUNISH.NICKNAME.YELLOW", "PUNISH.NICKNAME.RED")
+ .setEmoji("PUNISH.NICKNAME.YELLOW")
.setTitle("Tag create")
.setDescription(
keyValueList({
@@ -74,18 +74,9 @@
)
.setColor("Warning")
.setInverted(true)
+ .setFailedMessage("No changes were made", "Success", "PUNISH.NICKNAME.GREEN")
.send();
- if (confirmation.cancelled) return;
- if (!confirmation.success)
- return await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setTitle("Tag Create")
- .setDescription("No changes were made")
- .setStatus("Success")
- .setEmoji("PUNISH.NICKNAME.GREEN")
- ]
- });
+ if (confirmation.cancelled || !confirmation.success) return;
try {
await client.database.guilds.write(interaction.guild!.id, {
[`tags.${name}`]: value
@@ -118,7 +109,7 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
if (!member.permissions.has("ManageMessages"))
- throw new Error("You must have the *Manage Messages* permission to use this command");
+ return "You must have the *Manage Messages* permission to use this command";
return true;
};
diff --git a/src/commands/tags/delete.ts b/src/commands/tags/delete.ts
index 6d76eb1..18143d3 100644
--- a/src/commands/tags/delete.ts
+++ b/src/commands/tags/delete.ts
@@ -27,7 +27,7 @@
ephemeral: true
});
const confirmation = await new confirmationMessage(interaction)
- .setEmoji("PUNISH.NICKNAME.YELLOW", "PUNISH.NICKNAME.RED")
+ .setEmoji("PUNISH.NICKNAME.YELLOW")
.setTitle("Tag Delete")
.setDescription(
keyValueList({
@@ -37,18 +37,9 @@
)
.setColor("Warning")
.setInverted(true)
+ .setFailedMessage("No changes were made", "Success", "PUNISH.NICKNAME.GREEN")
.send();
- if (confirmation.cancelled) return;
- if (!confirmation.success)
- return await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setTitle("Tag Delete")
- .setDescription("No changes were made")
- .setStatus("Success")
- .setEmoji("PUNISH.NICKNAME.GREEN")
- ]
- });
+ if (confirmation.cancelled || !confirmation.success) return;
try {
await client.database.guilds.write(interaction.guild!.id, null, ["tags." + name]);
await client.memory.forceUpdate(interaction.guild!.id);
@@ -80,7 +71,7 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember;
if (!member.permissions.has("ManageMessages"))
- throw new Error("You must have the *Manage Messages* permission to use this command");
+ return "You must have the *Manage Messages* permission to use this command";
return true;
};
diff --git a/src/commands/tags/edit.ts b/src/commands/tags/edit.ts
index 018a0bb..e15f9ac 100644
--- a/src/commands/tags/edit.ts
+++ b/src/commands/tags/edit.ts
@@ -19,9 +19,9 @@
const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (!interaction.guild) return;
- const name = interaction.options.getString("name");
- const value = interaction.options.getString("value") ?? "";
- const newname = interaction.options.getString("newname") ?? "";
+ const name = interaction.options.get("name")?.value as string;
+ const value = interaction.options.get("value")?.value as string;
+ const newname = interaction.options.get("newname")?.value as string;
if (!newname && !value)
return await interaction.reply({
embeds: [
@@ -79,7 +79,7 @@
ephemeral: true
});
const confirmation = await new confirmationMessage(interaction)
- .setEmoji("PUNISH.NICKNAME.YELLOW", "PUNISH.NICKNAME.RED")
+ .setEmoji("PUNISH.NICKNAME.YELLOW")
.setTitle("Tag Edit")
.setDescription(
keyValueList({
@@ -89,27 +89,18 @@
)
.setColor("Warning")
.setInverted(true)
+ .setFailedMessage("No changes were made", "Success", "PUNISH.NICKNAME.GREEN")
.send();
if (confirmation.cancelled) return;
- if (!confirmation.success)
- return await interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setTitle("Tag Edit")
- .setDescription("No changes were made")
- .setStatus("Success")
- .setEmoji("PUNISH.NICKNAME.GREEN")
- ]
- });
try {
const toSet: Record<string, string> = {};
const toUnset: string[] = [];
if (value) toSet[`tags.${name}`] = value;
if (newname) {
toUnset.push(`tags.${name}`);
- toSet[`tags.${newname}`] = data.tags[name];
+ toSet[`tags.${newname}`] = data.tags[name]!;
}
- await client.database.guilds.write(interaction.guild.id, toSet === {} ? null : toSet, toUnset);
+ await client.database.guilds.write(interaction.guild.id, Object.keys(toSet).length === 0 ? null : toSet, toUnset);
await client.memory.forceUpdate(interaction.guild!.id);
} catch (e) {
return await interaction.editReply({
@@ -138,7 +129,7 @@
const check = (interaction: CommandInteraction) => {
const member = interaction.member as GuildMember;
if (!member.permissions.has("ManageMessages"))
- throw new Error("You must have the *Manage Messages* permission to use this command");
+ return "You must have the *Manage Messages* permission to use this command";
return true;
};
diff --git a/src/commands/tags/list.ts b/src/commands/tags/list.ts
index aee4c71..f0563c7 100644
--- a/src/commands/tags/list.ts
+++ b/src/commands/tags/list.ts
@@ -1,21 +1,22 @@
-import { LoadingEmbed } from "./../../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../../utils/defaults.js";
import Discord, {
CommandInteraction,
Message,
ActionRowBuilder,
- Component,
ButtonBuilder,
MessageComponentInteraction,
EmbedBuilder,
- SelectMenuInteraction,
- MessageSelectOptionData,
- ButtonStyle
+ ButtonStyle,
+ ButtonComponent,
+ StringSelectMenuBuilder
} from "discord.js";
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import client from "../../utils/client.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import createPageIndicator from "../../utils/createPageIndicator.js";
+interface MessageSelectOptionData { label: string; value: string; description?: string; }
+
class Embed {
embed: Discord.EmbedBuilder = new EmbedBuilder();
@@ -45,9 +46,9 @@
const callback = async (interaction: CommandInteraction): Promise<void> => {
const data = await client.database.guilds.read(interaction.guild!.id);
- const tags = data.getKey("tags");
+ const tags = data.tags;
let strings = [];
- if (data === {}) strings = ["*No tags exist*"];
+ if (Object.keys(tags).length === 0) strings = ["*No tags exist*"];
else {
let string = "";
for (const tag in tags) {
@@ -86,8 +87,7 @@
let cancelled = false;
let timedOut = false;
while (!cancelled && !timedOut) {
- let selectPane: ActionRowBuilder[] = [];
-
+ let selectPane: ActionRowBuilder<ButtonBuilder | StringSelectMenuBuilder>[] = [];
if (selectPaneOpen) {
const options: MessageSelectOptionData[] = [];
pages.forEach((embed) => {
@@ -98,8 +98,8 @@
});
});
selectPane = [
- new ActionRowBuilder().addComponents([
- new Discord.SelectMenuBuilder()
+ new ActionRowBuilder<StringSelectMenuBuilder>().addComponents([
+ new Discord.StringSelectMenuBuilder()
.addOptions(options)
.setCustomId("page")
.setMaxValues(1)
@@ -107,12 +107,12 @@
])
];
}
- const em = new Discord.EmbedBuilder(pages[page]!.embed);
- em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page));
+ const em = new Discord.EmbedBuilder(pages[page]!.embed.data);
+ em.setDescription(em.data.description + "\n\n" + createPageIndicator(pages.length, page));
await interaction.editReply({
embeds: [em],
components: selectPane.concat([
- new ActionRowBuilder().addComponents([
+ new ActionRowBuilder<ButtonBuilder>().addComponents([
new ButtonBuilder()
.setCustomId("left")
.setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
@@ -137,32 +137,35 @@
});
let i: MessageComponentInteraction;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
continue;
}
i.deferUpdate();
- if ((i.component as Component).customId === "left") {
+ if ((i.component as ButtonComponent).customId === "left") {
if (page > 0) page--;
selectPaneOpen = false;
- } else if ((i.component as Component).customId === "right") {
+ } else if ((i.component as ButtonComponent).customId === "right") {
if (page < pages.length - 1) page++;
selectPaneOpen = false;
- } else if ((i.component as Component).customId === "select") {
+ } else if (i.customId === "select") {
selectPaneOpen = !selectPaneOpen;
- } else if ((i.component as Component).customId === "page") {
- page = parseInt((i as SelectMenuInteraction).values[0]!);
+ } else if (i.customId === "page" && i.isStringSelectMenu()) {
+ page = parseInt(i.values[0]!);
selectPaneOpen = false;
} else {
cancelled = true;
}
}
- const em = new Discord.EmbedBuilder(pages[page]!.embed);
+ const em = new Discord.EmbedBuilder(pages[page]!.embed.data);
if (timedOut) {
- em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page) + " | Message timed out");
+ em.setDescription(em.data.description + "\n\n" + createPageIndicator(pages.length, page) + " | Message timed out");
} else {
- em.setDescription(em.description + "\n\n" + createPageIndicator(pages.length, page) + " | Message closed");
+ em.setDescription(em.data.description + "\n\n" + createPageIndicator(pages.length, page) + " | Message closed");
}
await interaction.editReply({
embeds: [em],
diff --git a/src/commands/user/about.ts b/src/commands/user/about.ts
index aa45690..e43ecb7 100644
--- a/src/commands/user/about.ts
+++ b/src/commands/user/about.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed, Embed } from "./../../utils/defaultEmbeds.js";
+import { LoadingEmbed, Embed } from "../../utils/defaults.js";
import Discord, {
CommandInteraction,
GuildMember,
@@ -256,7 +256,10 @@
});
let i: MessageComponentInteraction;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch {
timedOut = true;
continue;
diff --git a/src/commands/user/avatar.ts b/src/commands/user/avatar.ts
index 5980072..88b3270 100644
--- a/src/commands/user/avatar.ts
+++ b/src/commands/user/avatar.ts
@@ -35,7 +35,7 @@
});
};
-const check = (_interaction: CommandInteraction) => {
+const check = () => {
return true;
};
diff --git a/src/commands/user/track.ts b/src/commands/user/track.ts
index 06f498a..0ab5c10 100644
--- a/src/commands/user/track.ts
+++ b/src/commands/user/track.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../../utils/defaults.js";
import Discord, { CommandInteraction, GuildMember, Message, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
@@ -166,7 +166,10 @@
})) as Message;
let component;
try {
- component = await m.awaitMessageComponent({ time: 300000 });
+ component = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
} catch (e) {
timedOut = true;
continue;
diff --git a/src/commands/verify.ts b/src/commands/verify.ts
index b9556a6..4fafe69 100644
--- a/src/commands/verify.ts
+++ b/src/commands/verify.ts
@@ -8,7 +8,7 @@
verify(interaction);
};
-const check = (_interaction: CommandInteraction) => {
+const check = () => {
return true;
};
diff --git a/src/config/emojis.json b/src/config/emojis.json
index cbf3dc3..4bdadee 100644
--- a/src/config/emojis.json
+++ b/src/config/emojis.json
@@ -42,7 +42,8 @@
"THREAD_CHANNEL": "990210005108158514",
"THREAD_PIPE": "990213168183779348",
"RULES": "990213153080115250",
- "FORUM": "1061706437526552716"
+ "FORUM": "1061706437526552716",
+ "CATEGORY": "1064943289708597348"
}
},
"CONTROL": {
diff --git a/src/context/messages/purgeto.ts b/src/context/messages/purgeto.ts
index e2ec6e4..df52e0b 100644
--- a/src/context/messages/purgeto.ts
+++ b/src/context/messages/purgeto.ts
@@ -1,9 +1,10 @@
import confirmationMessage from '../../utils/confirmationMessage.js';
import EmojiEmbed from '../../utils/generateEmojiEmbed.js';
-import { LoadingEmbed } from './../../utils/defaultEmbeds.js';
+import { LoadingEmbed } from '../../utils/defaults.js';
import Discord, { ActionRowBuilder, ButtonBuilder, ButtonStyle, ContextMenuCommandBuilder, GuildTextBasedChannel, MessageContextMenuCommandInteraction } from "discord.js";
import client from "../../utils/client.js";
import getEmojiByName from '../../utils/getEmojiByName.js';
+import { JSONTranscriptFromMessageArray, JSONTranscriptToHumanReadable } from "../../utils/logTranscripts.js";
const command = new ContextMenuCommandBuilder()
.setName("Purge up to here")
@@ -12,7 +13,7 @@
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 });
+ component = m.awaitMessageComponent({ time: 200000, filter: (i) => i.user.id === member.id && i.channel!.id === m.channel.id });
} catch (e) {
return false;
}
@@ -120,6 +121,7 @@
)
.setColor("Danger")
.addReasonButton(reason ?? "")
+ .setFailedMessage("No changes were made", "Success", "CHANNEL.PURGE.GREEN")
.send(true)
reason = reason ?? ""
if (confirmation.cancelled) timedOut = true;
@@ -130,16 +132,7 @@
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;
- }
+ if (timedOut || !confirmation.success) 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
@@ -191,31 +184,9 @@
}
};
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 transcript = JSONTranscriptToHumanReadable(JSONTranscriptFromMessageArray(deleted.map((m) => m as Discord.Message))!);
const attachmentObject = {
- attachment: Buffer.from(out),
+ attachment: Buffer.from(transcript),
name: `purge-${channel.id}-${Date.now()}.txt`,
description: "Purge log"
};
@@ -240,7 +211,7 @@
let component;
try {
component = await m.awaitMessageComponent({
- filter: (m) => m.user.id === interaction.user.id,
+ filter: (m) => m.user.id === interaction.user.id && m.channel!.id === interaction.channel!.id,
time: 300000
});
} catch {
diff --git a/src/events/guildMemberUpdate.ts b/src/events/guildMemberUpdate.ts
index 0ebfc3d..440d786 100644
--- a/src/events/guildMemberUpdate.ts
+++ b/src/events/guildMemberUpdate.ts
@@ -1,4 +1,4 @@
-import type { GuildAuditLogsEntry, GuildMember } from "discord.js";
+import { AuditLogEvent, GuildAuditLogsEntry, GuildMember } from "discord.js";
import type { NucleusClient } from "../utils/client.js";
export const event = "guildMemberUpdate";
@@ -6,9 +6,9 @@
export async function callback(client: NucleusClient, before: GuildMember, after: GuildMember) {
try {
const { log, NucleusColors, entry, renderUser, renderDelta, getAuditLog } = client.logger;
- const auditLog = await getAuditLog(after.guild, "MEMBER_UPDATE");
+ const auditLog = await getAuditLog(after.guild, AuditLogEvent.MemberUpdate);
const audit = auditLog.entries.filter((entry: GuildAuditLogsEntry) => entry.target!.id === after.id).first();
- if (audit.executor.id === client.user.id) return;
+ if (audit.executor.id === client.user!.id) return;
if (before.nickname !== after.nickname) {
await client.database.history.create(
"nickname",
diff --git a/src/events/messageCreate.ts b/src/events/messageCreate.ts
index 2f3a077..69bc542 100644
--- a/src/events/messageCreate.ts
+++ b/src/events/messageCreate.ts
@@ -1,7 +1,7 @@
import type { NucleusClient } from "../utils/client.js";
import { LinkCheck, MalwareCheck, NSFWCheck, SizeCheck, TestString, TestImage } from "../reflex/scanners.js";
import logAttachment from "../premium/attachmentLogs.js";
-import createLogException from "../utils/createLogException.js";
+import { messageException } from "../utils/createTemporaryStorage.js";
import getEmojiByName from "../utils/getEmojiByName.js";
import client from "../utils/client.js";
import { callback as statsChannelUpdate } from "../reflex/statsChannelUpdate.js";
@@ -48,7 +48,7 @@
if (config.filters.invite.enabled) {
if (!config.filters.invite.allowed.channels.includes(message.channel.id)) {
if (/(?:https?:\/\/)?discord(?:app)?\.(?:com\/invite|gg)\/[a-zA-Z0-9]+\/?/.test(content)) {
- createLogException(message.guild.id, message.channel.id, message.id);
+ messageException(message.guild.id, message.channel.id, message.id);
message.delete();
const data = {
meta: {
@@ -84,7 +84,7 @@
!(message.channel instanceof ThreadChannel ? message.channel.parent?.nsfw : message.channel.nsfw)
) {
if (await NSFWCheck(url)) {
- createLogException(message.guild.id, message.channel.id, message.id);
+ messageException(message.guild.id, message.channel.id, message.id);
await message.delete();
const data = {
meta: {
@@ -119,7 +119,7 @@
config.filters.wordFilter.words.strict
);
if (check !== null) {
- createLogException(message.guild.id, message.channel.id, message.id);
+ messageException(message.guild.id, message.channel.id, message.id);
await message.delete();
const data = {
meta: {
@@ -149,7 +149,7 @@
if (config.filters.images.size) {
if (url.match(/\.+(webp|png|jpg)$/gi)) {
if (!(await SizeCheck(element))) {
- createLogException(message.guild.id, message.channel.id, message.id);
+ messageException(message.guild.id, message.channel.id, message.id);
await message.delete();
const data = {
meta: {
@@ -180,7 +180,7 @@
}
if (config.filters.malware) {
if (!(await MalwareCheck(url))) {
- createLogException(message.guild.id, message.channel.id, message.id);
+ messageException(message.guild.id, message.channel.id, message.id);
await message.delete();
const data = {
meta: {
@@ -212,7 +212,7 @@
const linkDetectionTypes = await LinkCheck(message);
if (linkDetectionTypes.length > 0) {
- createLogException(message.guild.id, message.channel.id, message.id);
+ messageException(message.guild.id, message.channel.id, message.id);
await message.delete();
const data = {
meta: {
@@ -244,7 +244,7 @@
config.filters.wordFilter.words.strict
);
if (check !== null) {
- createLogException(message.guild.id, message.channel.id, message.id);
+ messageException(message.guild.id, message.channel.id, message.id);
await message.delete();
const data = {
meta: {
@@ -293,7 +293,7 @@
if (config.filters.pings.roles) {
for (const roleId in message.mentions.roles) {
if (!config.filters.pings.allowed.roles.includes(roleId)) {
- createLogException(message.guild.id, message.channel.id, message.id);
+ messageException(message.guild.id, message.channel.id, message.id);
await message.delete();
const data = {
meta: {
@@ -319,7 +319,7 @@
}
}
if (message.mentions.users.size >= config.filters.pings.mass && config.filters.pings.mass) {
- createLogException(message.guild.id, message.channel.id, message.id);
+ messageException(message.guild.id, message.channel.id, message.id);
await message.delete();
const data = {
meta: {
diff --git a/src/index.ts b/src/index.ts
index aff1b2b..362b805 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -9,14 +9,9 @@
register();
runServer(client);
});
-process.on("unhandledRejection", (err) => {
- console.error(err);
-});
-process.on("uncaughtException", (err) => {
- console.error(err);
-});
+process.on("unhandledRejection", (err) => { console.error(err) });
+process.on("uncaughtException", (err) => { console.error(err) });
-if (config.enableDevelopment) { await client.login(config.developmentToken); }
-else { await client.login(config.token); }
+await client.login(config.enableDevelopment ? config.developmentToken : config.token)
await recordPerformance();
\ No newline at end of file
diff --git a/src/premium/createTranscript.ts b/src/premium/createTranscript.ts
index e755b83..f09ac92 100644
--- a/src/premium/createTranscript.ts
+++ b/src/premium/createTranscript.ts
@@ -113,7 +113,10 @@
}
let i;
try {
- i = await m.awaitMessageComponent({ time: 300000 });
+ i = await m.awaitMessageComponent({
+ time: 300000,
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ });
i.deferUpdate();
} catch {
return;
diff --git a/src/reflex/guide.ts b/src/reflex/guide.ts
index 842951c..b539aac 100644
--- a/src/reflex/guide.ts
+++ b/src/reflex/guide.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../utils/defaultEmbeds.js";
+import { LoadingEmbed } from "../utils/defaults.js";
import Discord, {
ActionRowBuilder,
ButtonBuilder,
@@ -225,7 +225,7 @@
}
let page = 0;
- const f = async (component: MessageComponentInteraction) => {
+ const publicFilter = async (component: MessageComponentInteraction) => {
return (component.member as Discord.GuildMember).permissions.has("MANAGE_GUILD");
};
@@ -293,10 +293,8 @@
try {
i = await m.awaitMessageComponent({
filter: interaction
- ? () => {
- return true;
- }
- : f,
+ ? (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ : publicFilter,
time: 300000
});
} catch (e) {
diff --git a/src/reflex/verify.ts b/src/reflex/verify.ts
index 2372130..4977c64 100644
--- a/src/reflex/verify.ts
+++ b/src/reflex/verify.ts
@@ -1,4 +1,4 @@
-import { LoadingEmbed } from "./../utils/defaultEmbeds.js";
+import { LoadingEmbed, unknownServerIcon } from "../utils/defaults.js";
import Discord, {
CommandInteraction,
GuildMember,
@@ -215,7 +215,7 @@
rName: role.name,
uName: interaction.member!.user.username,
gName: interaction.guild!.name,
- gIcon: interaction.guild!.iconURL({ extension: "png", size: 256 }) ?? "https://assets-global.website-files.com/6257adef93867e50d84d30e2/636e0a6a49cf127bf92de1e2_icon_clyde_blurple_RGB.png",
+ gIcon: interaction.guild!.iconURL({ extension: "png", size: 256 }) ?? unknownServerIcon,
interaction: interaction as MessageComponentInteraction
};
await interaction.editReply({
diff --git a/src/utils/calculate.ts b/src/utils/calculate.ts
index 98d82c3..0bd5a9f 100644
--- a/src/utils/calculate.ts
+++ b/src/utils/calculate.ts
@@ -23,7 +23,7 @@
const tickets = ["support", "report", "question", "issue", "suggestion", "other"];
-const toHexInteger = (permissions: string[], array?: string[]) => {
+const toHexInteger = (permissions: string[], array?: string[]): string => {
if (!array) {
array = logs;
}
@@ -35,18 +35,18 @@
return int.toString(16);
};
-const toHexArray = (permissionsHex: string, array?: string[]) => {
+const toHexArray = (permissionsHex: string, array?: string[]): string[] => {
if (!array) {
array = logs;
}
- const permissions = [];
+ const permissions: string[] = [];
const int = BigInt("0x" + permissionsHex)
.toString(2)
.split("")
.reverse();
for (const index in int) {
if (int[index] === "1" && array.length > parseInt(index)) {
- permissions.push(array[index]);
+ permissions.push(array[index]!);
}
}
return permissions;
diff --git a/src/utils/client.ts b/src/utils/client.ts
index 43cbe11..46d9f92 100644
--- a/src/utils/client.ts
+++ b/src/utils/client.ts
@@ -23,6 +23,7 @@
eventScheduler: EventScheduler;
performanceTest: PerformanceTest;
};
+ preloadPage: Record<string, {command: string, argument: string}> = {}; // e.g. { channelID: { command: privacy, page: 3}}
commands: Record<string, {
command: Discord.SlashCommandBuilder |
((builder: Discord.SlashCommandBuilder) => Discord.SlashCommandBuilder) |
diff --git a/src/utils/commandRegistration/register.ts b/src/utils/commandRegistration/register.ts
index d96ca90..fdae167 100644
--- a/src/utils/commandRegistration/register.ts
+++ b/src/utils/commandRegistration/register.ts
@@ -1,8 +1,10 @@
+import type { CommandInteraction } from 'discord.js';
import Discord, { Interaction, SlashCommandBuilder, ApplicationCommandType } from 'discord.js';
import config from "../../config/main.json" assert { type: "json" };
import client from "../client.js";
import fs from "fs";
-
+import EmojiEmbed from '../generateEmojiEmbed.js';
+import getEmojiByName from '../getEmojiByName.js';
const colours = {
red: "\x1b[31m",
@@ -174,17 +176,25 @@
});
}
-async function execute(check: Function | undefined, callback: Function | undefined, data: Interaction) {
+async function execute(check: Function | undefined, callback: Function | undefined, data: CommandInteraction) {
if (!callback) return;
if (check) {
let result;
try {
result = await check(data);
- } catch (e) {
- console.log(e);
+ } catch(e) {
result = false;
}
- if (!result) return;
+ if (result === false) return;
+ if (typeof result === "string") {
+ const { NucleusColors } = client.logger
+ return data.reply({embeds: [new EmojiEmbed()
+ .setTitle("")
+ .setDescription(result)
+ .setColor(NucleusColors.red)
+ .setEmoji(getEmojiByName("CONTROL.BLOCKCROSS"))
+ ]});
+ };
}
callback(data);
}
diff --git a/src/utils/confirmationMessage.ts b/src/utils/confirmationMessage.ts
index b3202e3..4d90676 100644
--- a/src/utils/confirmationMessage.ts
+++ b/src/utils/confirmationMessage.ts
@@ -183,7 +183,7 @@
let component;
try {
component = await m.awaitMessageComponent({
- filter: (m) => m.user.id === this.interaction.user.id,
+ filter: (m) => m.user.id === this.interaction.user.id && m.channel!.id === this.interaction.channel!.id,
time: 300000
});
} catch (e) {
diff --git a/src/utils/createLogException.ts b/src/utils/createLogException.ts
deleted file mode 100644
index 329f422..0000000
--- a/src/utils/createLogException.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import client from "./client.js";
-
-export default function (guild: string, channel: string, message: string) {
- client.noLog.push(`${guild}/${channel}/${message}`);
- setTimeout(() => {
- client.noLog = client.noLog.filter((i: string) => {
- return i !== `${guild}/${channel}/${message}`;
- });
- }, 500);
-}
diff --git a/src/utils/createTemporaryStorage.ts b/src/utils/createTemporaryStorage.ts
new file mode 100644
index 0000000..a684d9d
--- /dev/null
+++ b/src/utils/createTemporaryStorage.ts
@@ -0,0 +1,32 @@
+import client from "./client.js";
+
+function generalException(location: string) {
+ client.noLog.push(location);
+ setTimeout(() => {
+ client.noLog = client.noLog.filter((i: string) => {
+ return i !== location;
+ });
+ }, 1000);
+}
+
+export function messageException(guild: string, channel: string, message: string) {
+ generalException(`${guild}/${channel}/${message}`);
+}
+
+export function roleException(guild: string, user: string) {
+ generalException(`${guild}/${user}`);
+}
+
+export function preloadPage(target: string, command: string, message: string) {
+ client.preloadPage[target] = {
+ command: command,
+ argument: message
+ }
+ setTimeout(() => {
+ const object = Object.entries(client.preloadPage).filter((entry) => {
+ const [k, _] = entry
+ return k !== target;
+ })
+ client.preloadPage = Object.fromEntries(object);
+ }, 60 * 5 * 1000);
+}
\ No newline at end of file
diff --git a/src/utils/defaultEmbeds.ts b/src/utils/defaults.ts
similarity index 73%
rename from src/utils/defaultEmbeds.ts
rename to src/utils/defaults.ts
index a027c76..1331ce1 100644
--- a/src/utils/defaultEmbeds.ts
+++ b/src/utils/defaults.ts
@@ -1,3 +1,4 @@
+import type Discord from "discord.js";
import EmojiEmbed from "./generateEmojiEmbed.js";
import getEmojiByName from "./getEmojiByName.js";
@@ -15,6 +16,7 @@
title: string = "";
description = "";
pageId = 0;
+ componentsToSet: Discord.ActionRowBuilder<Discord.ButtonBuilder | Discord.StringSelectMenuBuilder>[] = [];
setEmbed(embed: EmojiEmbed) {
this.embed = embed;
@@ -32,6 +34,12 @@
this.pageId = pageId;
return this;
}
+ setComponents(components: Discord.ActionRowBuilder<Discord.ButtonBuilder | Discord.StringSelectMenuBuilder>[]) {
+ this.componentsToSet = components;
+ return this;
+ }
}
export { Embed };
+
+export const unknownServerIcon = "";
diff --git a/src/utils/getCommandMentionByName.ts b/src/utils/getCommandMentionByName.ts
new file mode 100644
index 0000000..b2b9937
--- /dev/null
+++ b/src/utils/getCommandMentionByName.ts
@@ -0,0 +1,22 @@
+import type Discord from "discord.js";
+import client from "./client.js";
+import config from "../config/main.json" assert { type: "json"};
+
+
+export const getCommandMentionByName = async (name: string): Promise<string> => {
+ const split = name.replaceAll("/", " ").split(" ")
+ const commandName: string = split[0]!;
+ let commandID: string;
+
+ const filterCommand = (command: Discord.ApplicationCommand) => command.name === commandName;
+
+ if (config.enableDevelopment) {
+ const developmentGuild = client.guilds.cache.get(config.developmentGuildID)!;
+ await developmentGuild.commands.fetch();
+ commandID = developmentGuild.commands.cache.filter(c => filterCommand(c)).first()!.id;
+ } else {
+ await client.application?.commands.fetch();
+ commandID = client.application?.commands.cache.filter(c => filterCommand(c)).first()!.id!;
+ }
+ return `</${split.join(" ")}:${commandID}>`;
+}
\ No newline at end of file
diff --git a/src/utils/log.ts b/src/utils/log.ts
index 7ab7903..3f46f86 100644
--- a/src/utils/log.ts
+++ b/src/utils/log.ts
@@ -26,6 +26,7 @@
return `${num1} -> ${num2} (${delta > 0 ? "+" : ""}${delta})`;
},
entry(value: string | number | null, displayValue: string): { value: string | null; displayValue: string } {
+ if (typeof value === "number") value = value.toString();
return { value: value, displayValue: displayValue };
},
renderChannel(channel: Discord.GuildChannel | Discord.ThreadChannel) {
diff --git a/src/utils/logTranscripts.ts b/src/utils/logTranscripts.ts
index d5ab0b2..0950664 100644
--- a/src/utils/logTranscripts.ts
+++ b/src/utils/logTranscripts.ts
@@ -1,3 +1,64 @@
-function JSONTranscriptFromMessageArray(messages: Discord.Message[]) {
-
+import type Discord from 'discord.js';
+
+export interface JSONTranscriptSchema {
+ messages: {
+ content: string | null;
+ attachments: {
+ url: string;
+ name: string;
+ size: number;
+ }[];
+ authorID: string;
+ authorUsername: string;
+ authorUsernameColor: string;
+ timestamp: string;
+ id: string;
+ edited: boolean;
+ }[];
+ channel: string;
+ guild: string;
+ timestamp: string;
+}
+
+
+export const JSONTranscriptFromMessageArray = (messages: Discord.Message[]): JSONTranscriptSchema | null => {
+ if (messages.length === 0) return null;
+ return {
+ guild: messages[0]!.guild!.id,
+ channel: messages[0]!.channel.id,
+ timestamp: Date.now().toString(),
+ messages: messages.map((message: Discord.Message) => {
+ return {
+ content: message.content,
+ attachments: message.attachments.map((attachment: Discord.Attachment) => {
+ return {
+ url: attachment.url,
+ name: attachment.name!,
+ size: attachment.size,
+ };
+ }),
+ authorID: message.author.id,
+ authorUsername: message.author.username + "#" + message.author.discriminator,
+ authorUsernameColor: message.member!.displayHexColor.toString(),
+ timestamp: message.createdTimestamp.toString(),
+ id: message.id,
+ edited: message.editedTimestamp ? true : false,
+ };
+ })
+ };
+}
+
+export const JSONTranscriptToHumanReadable = (data: JSONTranscriptSchema): string => {
+ let out = "";
+
+ for (const message of data.messages) {
+ const date = new Date(parseInt(message.timestamp));
+ out += `${message.authorUsername} (${message.authorID}) [${date}]`;
+ if (message.edited) out += " (edited)";
+ if (message.content) out += "\nContent:\n" + message.content.split("\n").map((line: string) => `\n> ${line}`).join("");
+ if (message.attachments.length > 0) out += "\nAttachments:\n" + message.attachments.map((attachment: { url: string; name: string; size: number; }) => `\n> [${attachment.name}](${attachment.url}) (${attachment.size} bytes)`).join("\n");
+
+ out += "\n\n";
+ }
+ return out;
}
\ No newline at end of file
diff --git a/src/utils/performanceTesting/record.ts b/src/utils/performanceTesting/record.ts
index 9f840af..95761e9 100644
--- a/src/utils/performanceTesting/record.ts
+++ b/src/utils/performanceTesting/record.ts
@@ -29,6 +29,7 @@
}
const record = async () => {
+ if (config.enableDevelopment) return;
const results = {
discord: discordPing(),
databaseRead: await databaseReadTime(),