Development (#11)
We need this NOW.
---------
Co-authored-by: PineaFan <ash@pinea.dev>
Co-authored-by: pineafan <pineapplefanyt@gmail.com>
Co-authored-by: PineappleFan <PineaFan@users.noreply.github.com>
Co-authored-by: Skyler <skyler3665@gmail.com>
diff --git a/src/commands/mod/_meta.ts b/src/commands/mod/_meta.ts
index af8006c..c5fcca5 100644
--- a/src/commands/mod/_meta.ts
+++ b/src/commands/mod/_meta.ts
@@ -5,4 +5,4 @@
const subcommand = await command(name, description, `mod`);
-export { name, description, subcommand as command };
+export { name, description, subcommand as command };
\ No newline at end of file
diff --git a/src/commands/mod/about.ts b/src/commands/mod/about.ts
index 130cdbc..0a9d962 100644
--- a/src/commands/mod/about.ts
+++ b/src/commands/mod/about.ts
@@ -3,17 +3,16 @@
import Discord, {
CommandInteraction,
GuildMember,
- Interaction,
Message,
ActionRowBuilder,
ButtonBuilder,
MessageComponentInteraction,
ModalSubmitInteraction,
ButtonStyle,
- StringSelectMenuInteraction,
TextInputStyle,
+ APIMessageComponentEmoji,
+ SlashCommandSubcommandBuilder
} from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import client from "../../utils/client.js";
@@ -167,8 +166,7 @@
.setLabel(value.text)
.setValue(key)
.setDefault(filteredTypes.includes(key))
- // @ts-expect-error
- .setEmoji(getEmojiByName(value.emoji, "id")) // FIXME: This gives a type error but is valid
+ .setEmoji(getEmojiByName(value.emoji, "id") as APIMessageComponentEmoji)
)))
]);
components = components.concat([new ActionRowBuilder<Discord.ButtonBuilder>().addComponents([
@@ -253,7 +251,7 @@
try {
i = await m.awaitMessageComponent({
time: 300000,
- filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id && i.message.id === m.id }
});
} catch (e) {
interaction.editReply({
@@ -269,9 +267,9 @@
timedOut = true;
continue;
}
- i.deferUpdate();
- if (i.customId === "filter") {
- filteredTypes = (i as StringSelectMenuInteraction).values;
+ await i.deferUpdate();
+ if (i.customId === "filter" && i.isStringSelectMenu()) {
+ filteredTypes = i.values;
pageIndex = null;
refresh = true;
}
@@ -359,7 +357,7 @@
try {
i = await m.awaitMessageComponent({
time: 300000,
- filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id }
+ filter: (i) => { return i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id && i.message.id === m.id }
});
} catch (e) {
timedOut = true;
@@ -402,17 +400,12 @@
});
let out;
try {
- out = await modalInteractionCollector(
- m,
- (m: Interaction) =>
- (m as MessageComponentInteraction | ModalSubmitInteraction).channelId === interaction.channelId,
- (m) => m.customId === "modify"
- );
+ out = await modalInteractionCollector(m, interaction.user);
} catch (e) {
timedOut = true;
continue;
}
- if (out === null) {
+ if (out === null || out.isButton()) {
continue;
} else if (out instanceof ModalSubmitInteraction) {
let toAdd = out.fields.getTextInputValue("note") || null;
@@ -423,7 +416,7 @@
continue;
}
} else if (i.customId === "history") {
- i.deferUpdate();
+ await i.deferUpdate();
if (!(await showHistory(member, interaction))) return;
}
}
@@ -436,6 +429,8 @@
return true;
};
-export { command };
-export { callback };
-export { check };
+export { command, callback, check };
+export const metadata = {
+ longDescription: "Shows the moderation history (all previous bans, kicks, warns etc.), and moderator notes for a user.",
+ premiumOnly: true,
+}
diff --git a/src/commands/mod/ban.ts b/src/commands/mod/ban.ts
index 70e904c..e8309fb 100644
--- a/src/commands/mod/ban.ts
+++ b/src/commands/mod/ban.ts
@@ -1,11 +1,11 @@
-import Discord, { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, User, ButtonStyle } from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import Discord, { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, User, ButtonStyle, SlashCommandSubcommandBuilder } from "discord.js";
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 { LinkWarningFooter } from "../../utils/defaults.js";
+import getEmojiByName from "../../utils/getEmojiByName.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
@@ -26,7 +26,7 @@
const callback = async (interaction: CommandInteraction): Promise<void> => {
if (!interaction.guild) return;
const { renderUser } = client.logger;
- // TODO:[Modals] Replace this with a modal
+ // TODO:[Modals] Replace the command arguments with a modal
let reason = null;
let notify = true;
let confirmation;
@@ -123,17 +123,20 @@
calculateType: "guildMemberPunish",
color: NucleusColors.red,
emoji: "PUNISH.BAN.RED",
- timestamp: new Date().getTime()
+ timestamp: Date.now()
},
list: {
memberId: entry(member.user.id, `\`${member.user.id}\``),
name: entry(member.user.id, renderUser(member.user)),
- banned: entry(new Date().getTime().toString(), renderDelta(new Date().getTime())),
+ banned: entry(Date.now().toString(), renderDelta(Date.now())),
bannedBy: 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
},
+ separate: {
+ end: getEmojiByName("ICONS.NOTIFY." + (notify ? "ON" : "OFF")) + ` The user was ${notify ? "" : "not "}notified`
+ },
hidden: {
guild: interaction.guild.id
}
@@ -166,9 +169,12 @@
});
};
-const check = async (interaction: CommandInteraction) => {
+const check = async (interaction: CommandInteraction, partial: boolean = false) => {
if (!interaction.guild) return;
const member = interaction.member as GuildMember;
+ // Check if the user has ban_members permission
+ if (!member.permissions.has("BanMembers")) return "You do not have the *Ban Members* permission";
+ if(partial) return true;
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;
@@ -181,21 +187,23 @@
apply = apply as User
}
// Do not allow banning the owner
- if (member.id === interaction.guild.ownerId) throw new Error("You cannot ban the owner of the server");
+ if (member.id === interaction.guild.ownerId) return "You cannot ban 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");
+ if (!(mePos > applyPos)) return `I do not have a role higher than <@${apply.id}>`;
// Check if Nucleus has permission to ban
- if (!me.permissions.has("BanMembers")) 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";
// Do not allow banning Nucleus
- if (member.id === me.id) throw new Error("I cannot ban myself");
+ if (member.id === me.id) return "I cannot ban myself";
// 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("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");
+ if (!(memberPos > applyPos)) return `You do not have a role higher than <@${apply.id}>`;
// Allow ban
return true;
};
export { command, callback, check };
+export const metadata = {
+ longDescription: "Removes a member from the server - this will prevent them from rejoining until they are unbanned, and will delete a specified number of days of messages from them.",
+ premiumOnly: true,
+}
diff --git a/src/commands/mod/kick.ts b/src/commands/mod/kick.ts
index 380bcc9..059bdb2 100644
--- a/src/commands/mod/kick.ts
+++ b/src/commands/mod/kick.ts
@@ -1,13 +1,13 @@
import { LinkWarningFooter } from '../../utils/defaults.js';
-import { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
+import { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle, SlashCommandSubcommandBuilder } from "discord.js";
// @ts-expect-error
import humanizeDuration from "humanize-duration";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import type Discord from "discord.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import keyValueList from "../../utils/generateKeyValueList.js";
import client from "../../utils/client.js";
+import getEmojiByName from "../../utils/getEmojiByName.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder
@@ -102,8 +102,8 @@
await client.database.history.create("kick", interaction.guild.id, member.user, interaction.user, reason);
const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger;
const timeInServer = member.joinedTimestamp ? entry(
- (new Date().getTime() - member.joinedTimestamp).toString(),
- humanizeDuration(new Date().getTime() - member.joinedTimestamp, {
+ (Date.now() - member.joinedTimestamp).toString(),
+ humanizeDuration(Date.now() - member.joinedTimestamp, {
round: true
})
) : entry(null, "*Unknown*")
@@ -114,18 +114,21 @@
calculateType: "guildMemberPunish",
color: NucleusColors.red,
emoji: "PUNISH.KICK.RED",
- timestamp: new Date().getTime()
+ timestamp: Date.now()
},
list: {
memberId: entry(member.id, `\`${member.id}\``),
name: entry(member.id, renderUser(member.user)),
joined: undefined as (unknown | typeof entry),
- kicked: entry(new Date().getTime().toString(), renderDelta(new Date().getTime())),
+ kicked: entry(Date.now().toString(), renderDelta(Date.now())),
kickedBy: entry(interaction.user.id, renderUser(interaction.user)),
reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"),
timeInServer: timeInServer,
serverMemberCount: member.guild.memberCount
},
+ separate: {
+ end: getEmojiByName("ICONS.NOTIFY." + (notify ? "ON" : "OFF")) + ` The user was ${notify ? "" : "not "}notified`
+ },
hidden: {
guild: member.guild.id
}
@@ -168,30 +171,37 @@
});
};
-const check = (interaction: CommandInteraction) => {
+const check = (interaction: CommandInteraction, partial: boolean = false) => {
if (!interaction.guild) return;
+
const member = interaction.member as GuildMember;
+ // Check if the user has kick_members permission
+ if (!member.permissions.has("KickMembers")) return "You do not have the *Kick Members* permission";
+ if (partial) return true;
+
const me = interaction.guild.members.me!;
const apply = interaction.options.getMember("user") as 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;
+ // Check if Nucleus has permission to kick
+ if (!me.permissions.has("KickMembers")) return "I do not have the *Kick Members* permission";
+ // Allow the owner to kick anyone
+ if (member.id === interaction.guild.ownerId) return true;
// Do not allow kicking the owner
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)) return "I do not have a role higher than that member";
- // Check if Nucleus has permission to kick
- if (!me.permissions.has("KickMembers")) return "I do not have the *Kick Members* permission";
+ if (!(mePos > applyPos)) return `I do not have a role higher than <@${apply.id}>`;
// Do not allow kicking Nucleus
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")) return "You do not have the *Kick Members* permission";
// Check if the user is below on the role list
- if (!(memberPos > applyPos)) return "You do not have a role higher than that member";
+ if (!(memberPos > applyPos)) return `You do not have a role higher than <@${apply.id}>`;
// Allow kick
return true;
};
export { command, callback, check };
+export const metadata = {
+ longDescription: "Removes a member from the server. They will be able to rejoin if they have an invite link.",
+ premiumOnly: true,
+}
diff --git a/src/commands/mod/mute.ts b/src/commands/mod/mute.ts
index 86291e5..c795456 100644
--- a/src/commands/mod/mute.ts
+++ b/src/commands/mod/mute.ts
@@ -1,6 +1,6 @@
import { LinkWarningFooter, LoadingEmbed } from "../../utils/defaults.js";
import Discord, { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
@@ -103,7 +103,7 @@
let component;
try {
component = await m.awaitMessageComponent({
- filter: (m) => m.user.id === interaction.user.id,
+ filter: (i) => {return i.user.id === interaction.user.id && i.channelId === interaction.channelId},
time: 300000
});
} catch {
@@ -235,8 +235,8 @@
.setDescription(
`You have been muted in ${interaction.guild.name}` +
(reason ? ` for:\n${reason}` : ".\n*No reason was provided*") + "\n\n" +
- `You will be unmuted at: <t:${Math.round(new Date().getTime() / 1000) + muteTime}:D> at ` +
- `<t:${Math.round(new Date().getTime() / 1000) + muteTime}:T> (<t:${Math.round(new Date().getTime() / 1000) + muteTime
+ `You will be unmuted at: <t:${Math.round(Date.now() / 1000) + muteTime}:D> at ` +
+ `<t:${Math.round(Date.now() / 1000) + muteTime}:T> (<t:${Math.round(Date.now() / 1000) + muteTime
}:R>)` + "\n\n" +
(createAppealTicket
? `You can appeal this in the ticket created in <#${confirmation.components!["appeal"]!.response}>`
@@ -267,10 +267,10 @@
await member.timeout(muteTime * 1000, reason || "*No reason provided*");
if (config.moderation.mute.role !== null) {
await member.roles.add(config.moderation.mute.role);
- await client.database.eventScheduler.schedule("naturalUnmute", (new Date().getTime() + muteTime * 1000).toString(), {
+ await client.database.eventScheduler.schedule("naturalUnmute", (Date.now() + muteTime * 1000).toString(), {
guild: interaction.guild.id,
user: member.id,
- expires: new Date().getTime() + muteTime * 1000
+ expires: Date.now() + muteTime * 1000
});
}
} else {
@@ -282,7 +282,7 @@
try {
if (config.moderation.mute.role !== null) {
await member.roles.add(config.moderation.mute.role);
- await client.database.eventScheduler.schedule("unmuteRole", (new Date().getTime() + muteTime * 1000).toString(), {
+ await client.database.eventScheduler.schedule("unmuteRole", (Date.now() + muteTime * 1000).toString(), {
guild: interaction.guild.id,
user: member.id,
role: config.moderation.mute.role
@@ -325,19 +325,22 @@
calculateType: "guildMemberPunish",
color: NucleusColors.yellow,
emoji: "PUNISH.WARN.YELLOW",
- timestamp: new Date().getTime()
+ timestamp: Date.now()
},
list: {
memberId: entry(member.user.id, `\`${member.user.id}\``),
name: entry(member.user.id, renderUser(member.user)),
mutedUntil: entry(
- (new Date().getTime() + muteTime * 1000).toString(),
- renderDelta(new Date().getTime() + muteTime * 1000)
+ (Date.now() + muteTime * 1000).toString(),
+ renderDelta(Date.now() + muteTime * 1000)
),
- muted: entry(new Date().getTime.toString(), renderDelta(new Date().getTime() - 1000)),
+ muted: entry(new Date().getTime.toString(), renderDelta(Date.now() - 1000)),
mutedBy: entry(interaction.member!.user.id, renderUser(interaction.member!.user as Discord.User)),
reason: entry(reason, reason ? reason : "*No reason provided*")
},
+ separate: {
+ end: getEmojiByName("ICONS.NOTIFY." + (notify ? "ON" : "OFF")) + ` The user was ${notify ? "" : "not "}notified`
+ },
hidden: {
guild: interaction.guild.id
}
@@ -361,9 +364,12 @@
});
};
-const check = (interaction: CommandInteraction) => {
+const check = async (interaction: CommandInteraction, partial: boolean = false) => {
if (!interaction.guild) return;
const member = interaction.member as GuildMember;
+ // Check if the user has moderate_members permission
+ if (!member.permissions.has("ModerateMembers")) return "You do not have the *Moderate Members* permission";
+ if (partial) return true;
const me = interaction.guild.members.me!;
const apply = interaction.options.getMember("user") as GuildMember;
const memberPos = member.roles.cache.size > 1 ? member.roles.highest.position : 0;
@@ -372,20 +378,21 @@
// Do not allow muting the owner
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)) return "I do not have a role higher than that member";
+ if (!(mePos > applyPos)) return `I do not have a role higher than <@${apply.id}>`;
// Check if Nucleus has permission to mute
if (!me.permissions.has("ModerateMembers")) return "I do not have the *Moderate Members* permission";
// Do not allow muting Nucleus
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"))
- return "You do not have the *Moderate Members* permission";
// Check if the user is below on the role list
- if (!(memberPos > applyPos)) return "You do not have a role higher than that member";
+ if (!(memberPos > applyPos)) return `You do not have a role higher than <@${apply.id}>`;
// Allow mute
return true;
};
export { command, callback, check };
+export const metadata = {
+ longDescription: "Stops a member from being able to send messages or join voice channels for a specified amount of time.",
+ premiumOnly: true,
+}
diff --git a/src/commands/mod/nick.ts b/src/commands/mod/nick.ts
index 9dd9336..5511d19 100644
--- a/src/commands/mod/nick.ts
+++ b/src/commands/mod/nick.ts
@@ -1,16 +1,16 @@
import { LinkWarningFooter } from './../../utils/defaults.js';
import { ActionRowBuilder, ButtonBuilder, CommandInteraction, GuildMember, ButtonStyle, Message } from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import keyValueList from "../../utils/generateKeyValueList.js";
import client from "../../utils/client.js";
import { areTicketsEnabled, create } from "../../actions/createModActionTicket.js";
+import getEmojiByName from "../../utils/getEmojiByName.js";
const command = (builder: SlashCommandSubcommandBuilder) => builder
.setName("nick")
- // .setNameLocalizations({"ru": "name", "zh-CN": "nickname"})
.setDescription("Changes a users nickname")
.addUserOption((option) => option.setName("user").setDescription("The user to change").setRequired(true))
.addStringOption((option) =>
@@ -156,15 +156,18 @@
calculateType: "guildMemberUpdate",
color: NucleusColors.yellow,
emoji: "PUNISH.NICKNAME.YELLOW",
- timestamp: new Date().getTime()
+ timestamp: Date.now()
},
list: {
memberId: entry(member.id, `\`${member.id}\``),
before: entry(before, before ?? "*No nickname set*"),
after: entry(nickname ?? null, nickname ?? "*No nickname set*"),
- updated: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+ updated: entry(Date.now(), renderDelta(Date.now())),
updatedBy: entry(interaction.user.id, renderUser(interaction.user))
},
+ separate: {
+ end: getEmojiByName("ICONS.NOTIFY." + (notify ? "ON" : "OFF")) + ` The user was ${notify ? "" : "not "}notified`
+ },
hidden: {
guild: interaction.guild!.id
}
@@ -189,8 +192,11 @@
});
};
-const check = (interaction: CommandInteraction) => {
+const check = async (interaction: CommandInteraction, partial: boolean = false) => {
const member = interaction.member as GuildMember;
+ // Check if the user has manage_nicknames permission
+ if (!member.permissions.has("ManageNicknames")) return "You do not have the *Manage Nicknames* permission";
+ if (partial) return true;
const me = interaction.guild!.members.me!;
const apply = interaction.options.getMember("user") as GuildMember;
const memberPos = member.roles.cache.size ? member.roles.highest.position : 0;
@@ -200,20 +206,21 @@
// Do not allow any changing of the owner
if (member.id === interaction.guild.ownerId) return "You cannot change the owner's nickname";
// Check if Nucleus can change the nickname
- if (!(mePos > applyPos)) return "I do not have a role higher than that member";
+ if (!(mePos > applyPos)) return `I do not have a role higher than <@${apply.id}>`;
// Check if Nucleus has permission to change the nickname
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"))
- 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)) return "You do not have a role higher than that member";
+ if (!(memberPos > applyPos)) return `You do not have a role higher than <@${apply.id}>`;
// Allow change
return true;
};
export { command, callback, check };
+export const metadata = {
+ longDescription: "Changes the nickname of a member. This is the name that shows in the member list and on messages.",
+ premiumOnly: true,
+}
diff --git a/src/commands/mod/purge.ts b/src/commands/mod/purge.ts
index e6b4670..8644e26 100644
--- a/src/commands/mod/purge.ts
+++ b/src/commands/mod/purge.ts
@@ -1,6 +1,5 @@
-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 Discord, { CommandInteraction, GuildChannel, GuildMember, TextChannel, ButtonStyle, ButtonBuilder, Message } from "discord.js";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import keyValueList from "../../utils/generateKeyValueList.js";
@@ -30,7 +29,7 @@
if (!interaction.guild) return;
const user = (interaction.options.getMember("user") as GuildMember | null);
const channel = interaction.channel as GuildChannel;
- if (channel.isTextBased()) {
+ if (!channel.isTextBased()) {
return await interaction.reply({
embeds: [
new EmojiEmbed()
@@ -94,7 +93,7 @@
let component;
try {
component = m.awaitMessageComponent({
- filter: (m) => m.user.id === interaction.user.id && m.channel!.id === interaction.channel!.id,
+ filter: (i) => i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id && i.id === m.id,
time: 300000
});
} catch (e) {
@@ -148,7 +147,7 @@
calculateType: "messageDelete",
color: NucleusColors.red,
emoji: "CHANNEL.PURGE.RED",
- timestamp: new Date().getTime()
+ timestamp: Date.now()
},
list: {
memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
@@ -161,7 +160,8 @@
}
};
log(data);
- const transcript = JSONTranscriptToHumanReadable(JSONTranscriptFromMessageArray(deleted)!);
+ const newOut = await client.database.transcripts.createTranscript(deleted, interaction, interaction.member as GuildMember);
+ const transcript = client.database.transcripts.toHumanReadable(newOut);
const attachmentObject = {
attachment: Buffer.from(transcript),
name: `purge-${channel.id}-${Date.now()}.txt`,
@@ -188,7 +188,7 @@
let component;
try {
component = await m.awaitMessageComponent({
- filter: (m) => m.user.id === interaction.user.id && m.channel!.id === interaction.channel!.id,
+ filter: (i) => i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id && i.id === m.id,
time: 300000
});
} catch {
@@ -296,7 +296,7 @@
calculateType: "messageDelete",
color: NucleusColors.red,
emoji: "CHANNEL.PURGE.RED",
- timestamp: new Date().getTime()
+ timestamp: Date.now()
},
list: {
memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
@@ -309,35 +309,17 @@
}
};
log(data);
- let out = "";
- messages.reverse().forEach((message) => {
- if (!message) {
- out += "Unknown message\n\n"
- } else {
- const author = message.author ?? { username: "Unknown", discriminator: "0000", id: "Unknown" };
- out += `${author.username}#${author.discriminator} (${author.id}) [${new Date(
- message.createdTimestamp
- ).toISOString()}]\n`;
- if (message.content) {
- const lines = message.content.split("\n");
- lines.forEach((line) => {
- out += `> ${line}\n`;
- });
- }
- if (message.attachments.size > 0) {
- message.attachments.forEach((attachment) => {
- out += `Attachment > ${attachment.url}\n`;
- });
- }
- out += "\n\n";
- }
- });
- const attachmentObject = {
- attachment: Buffer.from(out),
- name: `purge-${channel.id}-${Date.now()}.txt`,
- description: "Purge log"
- };
- const m = (await interaction.editReply({
+ const messageArray: Message[] = messages.filter(message => !(
+ message!.components.some(
+ component => component.components.some(
+ child => child.customId?.includes("transcript") ?? false
+ )
+ )
+ )).map(message => message as Message);
+ const newOut = await client.database.transcripts.createTranscript(messageArray, interaction, interaction.member as GuildMember);
+
+ const code = await client.database.transcripts.create(newOut);
+ await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("CHANNEL.PURGE.GREEN")
@@ -347,62 +329,30 @@
],
components: [
new Discord.ActionRowBuilder<ButtonBuilder>().addComponents([
- new Discord.ButtonBuilder()
- .setCustomId("download")
- .setLabel("Download transcript")
- .setStyle(ButtonStyle.Success)
- .setEmoji(getEmojiByName("CONTROL.DOWNLOAD", "id"))
+ new ButtonBuilder().setLabel("View").setStyle(ButtonStyle.Link).setURL(`https://clicks.codes/nucleus/transcript?code=${code}`),
])
]
- })) as Discord.Message;
- let component;
- try {
- component = await m.awaitMessageComponent({
- filter: (m) => m.user.id === interaction.user.id && m.channel!.id === interaction.channel!.id,
- time: 300000
- });
- } catch {
- return;
- }
- if (component.customId === "download") {
- interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setEmoji("CHANNEL.PURGE.GREEN")
- .setTitle("Purge")
- .setDescription("Transcript uploaded above")
- .setStatus("Success")
- ],
- components: [],
- files: [attachmentObject]
- });
- } else {
- interaction.editReply({
- embeds: [
- new EmojiEmbed()
- .setEmoji("CHANNEL.PURGE.GREEN")
- .setTitle("Purge")
- .setDescription("Messages cleared")
- .setStatus("Success")
- ],
- components: []
- });
- }
+ });
}
};
-const check = (interaction: CommandInteraction) => {
+const check = (interaction: CommandInteraction, partial: boolean = false) => {
if (!interaction.guild) return false;
const member = interaction.member as GuildMember;
+ // Check if the user has manage_messages permission
+ if (!member.permissions.has("ManageMessages")) return "You do not have the *Manage Messages* permission";
+ if (partial) return true;
const me = interaction.guild.members.me!;
// Check if nucleus has 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")) return "You do not have the *Manage Messages* permission";
// Allow purge
return true;
};
export { command, callback, check };
+export const metadata = {
+ longDescription: "Deletes a specified amount of messages from a channel, optionally from a specific user. Without an amount, you can repeatedly choose a number of messages to delete.",
+ premiumOnly: true,
+}
diff --git a/src/commands/mod/slowmode.ts b/src/commands/mod/slowmode.ts
index 9792827..f282e82 100644
--- a/src/commands/mod/slowmode.ts
+++ b/src/commands/mod/slowmode.ts
@@ -1,7 +1,7 @@
// @ts-expect-error
import humanizeDuration from "humanize-duration";
import type { CommandInteraction, GuildMember, TextChannel } from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
import keyValueList from "../../utils/generateKeyValueList.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
@@ -47,7 +47,7 @@
}) + "Are you sure you want to set the slowmode in this channel?"
)
.setColor("Danger")
- .setFailedMessage("No changes were made", "Danger", "CHANNEL.SLOWMODE.ON")
+ .setFailedMessage("No changes were made", "Success", "CHANNEL.SLOWMODE.ON")
.send();
if (confirmation.cancelled || !confirmation.success) return;
try {
@@ -76,14 +76,19 @@
});
};
-const check = (interaction: CommandInteraction) => {
+const check = (interaction: CommandInteraction, partial: boolean = false) => {
const member = interaction.member as GuildMember;
- // Check if Nucleus can set the slowmode
- 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")) return "You do not have the *Manage Channels* permission";
+ if (partial) return true;
+ // Check if Nucleus can set the slowmode
+ if (!interaction.guild!.members.me!.permissions.has("ManageChannels")) return "I do not have the *Manage Channels* permission";
// Allow slowmode
return true;
};
export { command, callback, check };
+export const metadata = {
+ longDescription: "Stops members from being able to send messages without waiting a certain amount of time between messages.",
+ premiumOnly: true,
+}
diff --git a/src/commands/mod/softban.ts b/src/commands/mod/softban.ts
index 2787e91..1b404c9 100644
--- a/src/commands/mod/softban.ts
+++ b/src/commands/mod/softban.ts
@@ -1,11 +1,12 @@
import Discord, { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, User, ButtonStyle } from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
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 { LinkWarningFooter } from "../../utils/defaults.js";
+import getEmojiByName from "../../utils/getEmojiByName.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
@@ -124,17 +125,20 @@
calculateType: "guildMemberPunish",
color: NucleusColors.yellow,
emoji: "PUNISH.BAN.YELLOW",
- timestamp: new Date().getTime()
+ timestamp: Date.now()
},
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())),
+ softbanned: entry(Date.now().toString(), renderDelta(Date.now())),
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
},
+ separate: {
+ end: getEmojiByName("ICONS.NOTIFY." + (notify ? "ON" : "OFF")) + ` The user was ${notify ? "" : "not "}notified`
+ },
hidden: {
guild: interaction.guild.id
}
@@ -167,9 +171,12 @@
});
};
-const check = async (interaction: CommandInteraction) => {
+const check = async (interaction: CommandInteraction, partial: boolean = false) => {
if (!interaction.guild) return;
const member = interaction.member as GuildMember;
+ // Check if the user has ban_members permission
+ if (!member.permissions.has("BanMembers")) return "You do not have the *Ban Members* permission";
+ if (partial) return true;
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;
@@ -182,19 +189,17 @@
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");
+ if (member.id === interaction.guild.ownerId) return "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");
+ if (!(mePos > applyPos)) return `I do not have a role higher than <@${apply.id}>`;
// Check if Nucleus has permission to ban
- if (!me.permissions.has("BanMembers")) 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";
// Do not allow banning Nucleus
- if (member.id === me.id) throw new Error("I cannot softban myself");
+ if (member.id === me.id) return "I cannot softban myself";
// 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("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");
+ if (!(memberPos > applyPos)) return `You do not have a role higher than <@${apply.id}>`;
// Allow ban
return true;
};
diff --git a/src/commands/mod/unban.ts b/src/commands/mod/unban.ts
index 37fee99..40f4504 100644
--- a/src/commands/mod/unban.ts
+++ b/src/commands/mod/unban.ts
@@ -1,5 +1,5 @@
import type { CommandInteraction, GuildMember, User } from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import keyValueList from "../../utils/generateKeyValueList.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
@@ -57,12 +57,12 @@
calculateType: "guildMemberPunish",
color: NucleusColors.green,
emoji: "PUNISH.BAN.GREEN",
- timestamp: new Date().getTime()
+ timestamp: Date.now()
},
list: {
memberId: entry(member.id, `\`${member.id}\``),
name: entry(member.id, renderUser(member)),
- unbanned: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+ unbanned: entry(Date.now(), renderDelta(Date.now())),
unbannedBy: entry(interaction.user.id, renderUser(interaction.user)),
accountCreated: entry(member.createdTimestamp, renderDelta(member.createdTimestamp))
},
@@ -107,16 +107,17 @@
}
};
-const check = (interaction: CommandInteraction) => {
+const check = (interaction: CommandInteraction, partial: boolean = false) => {
if (!interaction.guild) return;
const member = interaction.member as GuildMember;
+ // Check if the user has ban_members permission
+ if (!member.permissions.has("BanMembers")) return "You do not have the *Ban Members* permission";
+ if (partial) return true;
const me = interaction.guild.members.me!;
// Check if Nucleus can unban members
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("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 e2585e1..8562c4c 100644
--- a/src/commands/mod/unmute.ts
+++ b/src/commands/mod/unmute.ts
@@ -1,9 +1,10 @@
import type { CommandInteraction, GuildMember } from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import keyValueList from "../../utils/generateKeyValueList.js";
import client from "../../utils/client.js";
+import getEmojiByName from "../../utils/getEmojiByName.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder
@@ -105,14 +106,17 @@
calculateType: "guildMemberPunish",
color: NucleusColors.green,
emoji: "PUNISH.MUTE.GREEN",
- timestamp: new Date().getTime()
+ timestamp: Date.now()
},
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())),
+ unmuted: entry(Date.now().toString(), renderDelta(Date.now())),
unmutedBy: entry(interaction.user.id, renderUser(interaction.user))
},
+ separate: {
+ end: getEmojiByName("ICONS.NOTIFY." + (notify ? "ON" : "OFF")) + ` The user was ${notify ? "" : "not "}notified`
+ },
hidden: {
guild: interaction.guild.id
}
@@ -131,9 +135,13 @@
});
};
-const check = (interaction: CommandInteraction) => {
+const check = (interaction: CommandInteraction, partial: boolean = false) => {
if (!interaction.guild) return;
const member = interaction.member as GuildMember;
+ // Check if the user has moderate_members permission
+ if (!member.permissions.has("ModerateMembers"))
+ return "You do not have the *Moderate Members* permission";
+ if (partial) return true;
const me = interaction.guild.members.me!;
const apply = interaction.options.getMember("user") as GuildMember;
const memberPos = member.roles.cache.size > 1 ? member.roles.highest.position : 0;
@@ -142,16 +150,13 @@
// Do not allow unmuting the owner
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)) return "I do not have a role higher than that member";
+ if (!(mePos > applyPos)) return `I do not have a role higher than <@${apply.id}>`;
// Check if Nucleus has permission to unmute
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"))
- return "You do not have the *Moderate Members* permission";
// Check if the user is below on the role list
- if (!(memberPos > applyPos)) return "You do not have a role higher than that member";
+ if (!(memberPos > applyPos)) return `You do not have a role higher than <@${apply.id}>`;
// Allow unmute
return true;
};
diff --git a/src/commands/mod/viewas.ts b/src/commands/mod/viewas.ts
index b176dd4..ef62816 100644
--- a/src/commands/mod/viewas.ts
+++ b/src/commands/mod/viewas.ts
@@ -7,9 +7,10 @@
ButtonStyle,
NonThreadGuildBasedChannel,
StringSelectMenuOptionBuilder,
- StringSelectMenuBuilder
+ StringSelectMenuBuilder,
+ APIMessageComponentEmoji
} from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
import type { GuildBasedChannel } from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
@@ -126,8 +127,7 @@
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
+ .setEmoji(getEmojiByName("ICONS.CHANNEL.CATEGORY", "id") as APIMessageComponentEmoji) // Again, this is valid but TS doesn't think so
.setDefault((set * 25 + i) === page)
}))
)}
@@ -157,19 +157,19 @@
});
let i;
try {
- i = await m.awaitMessageComponent({filter: (i) => i.user.id === interaction.user.id, time: 30000});
+ i = await m.awaitMessageComponent({filter: (i) => i.user.id === interaction.user.id && i.message.id === m.id, time: 30000});
} catch (e) {
closed = true;
continue;
}
- i.deferUpdate();
+ await 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 check = (interaction: CommandInteraction, _partial: boolean = false) => {
const member = interaction.member as GuildMember;
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 38aa4ad..ea4f084 100644
--- a/src/commands/mod/warn.ts
+++ b/src/commands/mod/warn.ts
@@ -1,10 +1,11 @@
import Discord, { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
-import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import type { SlashCommandSubcommandBuilder } from "discord.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import keyValueList from "../../utils/generateKeyValueList.js";
import { create, areTicketsEnabled } from "../../actions/createModActionTicket.js";
import client from "../../utils/client.js";
+import getEmojiByName from "../../utils/getEmojiByName.js";
import { LinkWarningFooter } from "../../utils/defaults.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
@@ -116,7 +117,7 @@
calculateType: "guildMemberPunish",
color: NucleusColors.yellow,
emoji: "PUNISH.WARN.YELLOW",
- timestamp: new Date().getTime()
+ timestamp: Date.now()
},
list: {
user: entry(
@@ -124,7 +125,10 @@
renderUser((interaction.options.getMember("user") as GuildMember).user)
),
warnedBy: entry(interaction.member!.user.id, renderUser(interaction.member!.user as Discord.User)),
- reason: reason ? `\n> ${reason}` : "*No reason provided*"
+ reason: reason ? reason : "*No reason provided*"
+ },
+ separate: {
+ end: getEmojiByName("ICONS.NOTIFY." + (notify ? "ON" : "OFF")) + ` The user was ${notify ? "" : "not "}notified`
},
hidden: {
guild: interaction.guild.id
@@ -186,7 +190,7 @@
let component;
try {
component = await m.awaitMessageComponent({
- filter: (m) => m.user.id === interaction.user.id && m.channel!.id === interaction.channel!.id,
+ filter: (i) => i.user.id === interaction.user.id && i.channel!.id === interaction.channel!.id && i.id === m.id,
time: 300000
});
} catch (e) {
@@ -275,9 +279,12 @@
}
};
-const check = (interaction: CommandInteraction) => {
+const check = (interaction: CommandInteraction, partial: boolean = false) => {
if (!interaction.guild) return;
const member = interaction.member as GuildMember;
+ if (!member.permissions.has("ModerateMembers"))
+ return "You do not have the *Moderate Members* permission";
+ if(partial) return true;
const apply = interaction.options.getMember("user") as GuildMember | null;
if (apply === null) return "That member is not in the server";
const memberPos = member.roles.cache.size ? member.roles.highest.position : 0;
@@ -287,10 +294,8 @@
// 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"))
- return "You do not have the *Moderate Members* permission";
// Check if the user is below on the role list
- if (!(memberPos > applyPos)) return "You do not have a role higher than that member";
+ if (!(memberPos > applyPos)) return `You do not have a role higher than <@${apply.id}>`;
// Allow warn
return true;
};