Start of context menus

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

@ -1,4 +1,4 @@
import Discord, { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; import Discord, { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction } from "discord.js";
import { tickets, toHexArray } from "../../utils/calculate.js"; import { tickets, toHexArray } from "../../utils/calculate.js";
import client from "../../utils/client.js"; import client from "../../utils/client.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
@ -9,7 +9,8 @@ function capitalize(s: string) {
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) { export default async function (interaction: CommandInteraction) {
if (!interaction.guild) return;
const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger; const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger;
const config = await client.database.guilds.read(interaction.guild.id); const config = await client.database.guilds.read(interaction.guild.id);

@ -4,9 +4,10 @@ import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import getEmojiByName from "../../utils/getEmojiByName.js"; import getEmojiByName from "../../utils/getEmojiByName.js";
export default async function (interaction: Discord.CommandInteraction) { export default async function (interaction: Discord.CommandInteraction) {
if (!interaction.guild) return;
const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger; const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger;
const config = await client.database.guilds.read(interaction.guild!.id); const config = await client.database.guilds.read(interaction.guild.id);
let thread = false; let thread = false;
if (interaction.channel instanceof Discord.ThreadChannel) thread = true; if (interaction.channel instanceof Discord.ThreadChannel) thread = true;
const threadChannel = interaction.channel as Discord.ThreadChannel; const threadChannel = interaction.channel as Discord.ThreadChannel;

@ -24,6 +24,7 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
const callback = async (interaction: CommandInteraction): Promise<void> => { const callback = async (interaction: CommandInteraction): Promise<void> => {
if (!interaction.guild) return;
const { renderUser } = client.logger; const { renderUser } = client.logger;
// TODO:[Modals] Replace this with a modal // TODO:[Modals] Replace this with a modal
let reason = null; let reason = null;
@ -69,7 +70,7 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
reason = reason.length ? reason : null reason = reason.length ? reason : null
let dmSent = false; let dmSent = false;
let dmMessage; let dmMessage;
const config = await client.database.guilds.read(interaction.guild!.id); const config = await client.database.guilds.read(interaction.guild.id);
try { try {
if (notify) { if (notify) {
const messageData: { const messageData: {
@ -81,7 +82,7 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
.setEmoji("PUNISH.BAN.RED") .setEmoji("PUNISH.BAN.RED")
.setTitle("Banned") .setTitle("Banned")
.setDescription( .setDescription(
`You have been banned in ${interaction.guild!.name}` + (reason ? ` for:\n> ${reason}` : ".") `You have been banned in ${interaction.guild.name}` + (reason ? ` for:\n> ${reason}` : ".")
) )
.setStatus("Danger") .setStatus("Danger")
], ],
@ -110,7 +111,7 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
deleteMessageSeconds: days * 24 * 60 * 60, deleteMessageSeconds: days * 24 * 60 * 60,
reason: reason ?? "*No reason provided*" reason: reason ?? "*No reason provided*"
}); });
await client.database.history.create("ban", interaction.guild!.id, member.user, interaction.user, reason); await client.database.history.create("ban", interaction.guild.id, member.user, interaction.user, reason);
const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger; const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger;
const data = { const data = {
meta: { meta: {
@ -128,10 +129,10 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
bannedBy: entry(interaction.user.id, renderUser(interaction.user)), bannedBy: entry(interaction.user.id, renderUser(interaction.user)),
reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"), reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"),
accountCreated: entry(member.user.createdAt.toString(), renderDelta(member.user.createdAt.getTime())), accountCreated: entry(member.user.createdAt.toString(), renderDelta(member.user.createdAt.getTime())),
serverMemberCount: interaction.guild!.memberCount serverMemberCount: interaction.guild.memberCount
}, },
hidden: { hidden: {
guild: interaction.guild!.id guild: interaction.guild.id
} }
}; };
log(data); log(data);
@ -175,20 +176,21 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
}; };
const check = async (interaction: CommandInteraction) => { const check = async (interaction: CommandInteraction) => {
if (!interaction.guild) return;
const member = interaction.member as GuildMember; const member = interaction.member as GuildMember;
const me = interaction.guild!.members.me!; const me = interaction.guild.members.me!;
let apply = interaction.options.getUser("user") as User | GuildMember; let apply = interaction.options.getUser("user") as User | GuildMember;
const memberPos = member.roles.cache.size > 1 ? member.roles.highest.position : 0; 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 mePos = me.roles.cache.size > 1 ? me.roles.highest.position : 0;
let applyPos = 0 let applyPos = 0
try { try {
apply = await interaction.guild!.members.fetch(apply.id) as GuildMember apply = await interaction.guild.members.fetch(apply.id) as GuildMember
applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0; applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0;
} catch { } catch {
apply = apply as User apply = apply as User
} }
// Do not allow banning the owner // 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) throw new Error("You cannot ban the owner of the server");
// Check if Nucleus can ban the member // 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)) throw new Error("I do not have a role higher than that member");
// Check if Nucleus has permission to ban // Check if Nucleus has permission to ban
@ -196,7 +198,7 @@ const check = async (interaction: CommandInteraction) => {
// Do not allow banning Nucleus // Do not allow banning Nucleus
if (member.id === me.id) throw new Error("I cannot ban myself"); if (member.id === me.id) throw new Error("I cannot ban myself");
// Allow the owner to ban anyone // Allow the owner to ban anyone
if (member.id === interaction.guild!.ownerId) return true; if (member.id === interaction.guild.ownerId) return true;
// Check if the user has ban_members permission // Check if the user has ban_members permission
if (!member.permissions.has("BanMembers")) 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 // Check if the user is below on the role list

@ -1,7 +1,9 @@
import { LinkWarningFooter } from './../../utils/defaultEmbeds';
import { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; import { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
// @ts-expect-error // @ts-expect-error
import humanizeDuration from "humanize-duration"; import humanizeDuration from "humanize-duration";
import type { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import type Discord from "discord.js";
import confirmationMessage from "../../utils/confirmationMessage.js"; import confirmationMessage from "../../utils/confirmationMessage.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import keyValueList from "../../utils/generateKeyValueList.js"; import keyValueList from "../../utils/generateKeyValueList.js";
@ -14,77 +16,103 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.addUserOption((option) => option.setName("user").setDescription("The user to kick").setRequired(true)); .addUserOption((option) => option.setName("user").setDescription("The user to kick").setRequired(true));
const callback = async (interaction: CommandInteraction): Promise<unknown> => { const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (!interaction.guild) return;
const { renderUser } = client.logger; const { renderUser } = client.logger;
// TODO:[Modals] Replace this with a modal // TODO:[Modals] Replace this with a modal
let reason = null; let reason: string | null = null;
let notify = true; let notify = true;
let confirmation; let confirmation;
let timedOut = false; let timedOut = false;
let chosen = false; let success = false;
while (!timedOut && !chosen) { do {
confirmation = await new confirmationMessage(interaction) confirmation = await new confirmationMessage(interaction)
.setEmoji("PUNISH.KICK.RED") .setEmoji("PUNISH.KICK.RED")
.setTitle("Kick") .setTitle("Kick")
.setDescription( .setDescription(
keyValueList({ keyValueList({
user: renderUser(interaction.options.getUser("user")), user: renderUser(interaction.options.getUser("user")!),
reason: reason ? "\n> " + (reason).replaceAll("\n", "\n> ") : "*No reason provided*" reason: reason ? "\n> " + (reason).replaceAll("\n", "\n> ") : "*No reason provided*"
}) + }) +
`The user **will${notify ? "" : " not"}** be notified\n\n` +
`Are you sure you want to kick <@!${(interaction.options.getMember("user") as GuildMember).id}>?` `Are you sure you want to kick <@!${(interaction.options.getMember("user") as GuildMember).id}>?`
) )
.setColor("Danger") .setColor("Danger")
.addCustomBoolean(
"notify",
"Notify user",
false,
null,
"The user will be sent a DM",
"ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
notify
)
.addReasonButton(reason ?? "") .addReasonButton(reason ?? "")
.send(reason !== null); .send(reason !== null);
reason = reason ?? ""; reason = reason ?? "";
if (confirmation.cancelled) timedOut = true; if (confirmation.cancelled) timedOut = true;
else if (confirmation.success !== undefined) chosen = true; else if (confirmation.success !== undefined) success = true;
else if (confirmation.newReason) reason = confirmation.newReason; else if (confirmation.newReason) reason = confirmation.newReason;
else if (confirmation.components) { else if (confirmation.components) {
notify = confirmation.components["notify"]!.active; notify = confirmation.components["notify"]!.active;
} }
} } while (!timedOut && !success)
if (timedOut) return; if (timedOut) return;
let dmd = false; if (!confirmation.success) {
let dm; await interaction.editReply({
const config = await client.database.guilds.read(interaction.guild!.id); embeds: [
new EmojiEmbed()
.setEmoji("PUNISH.KICK.GREEN")
.setTitle("Kick")
.setDescription("No changes were made")
.setStatus("Success")
],
components: []
});
return;
}
let dmSent = false;
let dmMessage;
const config = await client.database.guilds.read(interaction.guild.id);
try { try {
if (notify) { if (notify) {
dm = await (interaction.options.getMember("user") as GuildMember).send({ const messageData: {
embeds: EmojiEmbed[];
components: ActionRowBuilder<ButtonBuilder>[];
} = {
embeds: [ embeds: [
new EmojiEmbed() new EmojiEmbed()
.setEmoji("PUNISH.KICK.RED") .setEmoji("PUNISH.KICK.RED")
.setTitle("Kicked") .setTitle("Kicked")
.setDescription( .setDescription(
`You have been kicked in ${interaction.guild!.name}` + (reason ? ` for:\n> ${reason}` : ".") `You have been kicked from ${interaction.guild.name}` +
(reason ? ` for:\n> ${reason}` : ".\n*No reason was provided.*")
) )
.setStatus("Danger") .setStatus("Danger")
], ],
components: [ components: []
new ActionRowBuilder().addComponents( };
config.moderation.kick.text if (config.moderation.kick.text && config.moderation.kick.link) {
? [ messageData.embeds[0]!.setFooter(LinkWarningFooter)
new ButtonBuilder() messageData.components.push(new ActionRowBuilder<Discord.ButtonBuilder>()
.setStyle(ButtonStyle.Link) .addComponents(new ButtonBuilder()
.setLabel(config.moderation.kick.text) .setStyle(ButtonStyle.Link)
.setURL(config.moderation.kick.link) .setLabel(config.moderation.kick.text)
] .setURL(config.moderation.kick.link)
: [] )
) )
] }
}); dmMessage = await (interaction.options.getMember("user") as GuildMember).send(messageData);
dmd = true; dmSent = true;
} }
} catch { } catch {
dmd = false; dmSent = false;
} }
try { try {
(interaction.options.getMember("user") as GuildMember).kick(reason ?? "No reason provided."); (interaction.options.getMember("user") as GuildMember).kick(reason || "No reason provided");
const member = interaction.options.getMember("user") as GuildMember; const member = interaction.options.getMember("user") as GuildMember;
await client.database.history.create("kick", interaction.guild!.id, member.user, interaction.user, reason); await client.database.history.create("kick", interaction.guild.id, member.user, interaction.user, reason);
const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger; const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger;
const timeInServer = member.joinedTimestamp ? entry( const timeInServer = member.joinedTimestamp ? entry(
new Date().getTime() - member.joinedTimestamp, (new Date().getTime() - member.joinedTimestamp).toString(),
humanizeDuration(new Date().getTime() - member.joinedTimestamp, { humanizeDuration(new Date().getTime() - member.joinedTimestamp, {
round: true round: true
}) })
@ -101,18 +129,27 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
list: { list: {
memberId: entry(member.id, `\`${member.id}\``), memberId: entry(member.id, `\`${member.id}\``),
name: entry(member.id, renderUser(member.user)), name: entry(member.id, renderUser(member.user)),
joined: entry(member.joinedAt, renderDelta(member.joinedAt)), joined: undefined as (unknown | typeof entry),
kicked: entry(new Date().getTime(), renderDelta(new Date().getTime())), kicked: entry(new Date().getTime().toString(), renderDelta(new Date().getTime())),
kickedBy: entry(interaction.user.id, renderUser(interaction.user)), kickedBy: entry(interaction.user.id, renderUser(interaction.user)),
reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"), reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"),
timeInServer: timeInServer, timeInServer: timeInServer,
accountCreated: entry(member.user.createdAt, renderDelta(member.user.createdAt)),
serverMemberCount: member.guild.memberCount serverMemberCount: member.guild.memberCount
}, },
hidden: { hidden: {
guild: member.guild.id guild: member.guild.id
} }
}; };
if (member.joinedTimestamp) {
data.list.joined = entry(member.joinedTimestamp.toString(), renderDelta(member.joinedTimestamp))
}
await client.database.history.create(
"kick",
interaction.guild.id,
member.user,
interaction.user,
reason
)
log(data); log(data);
} catch { } catch {
await interaction.editReply({ await interaction.editReply({
@ -125,10 +162,10 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
], ],
components: [] components: []
}); });
if (dmd && dm) await dm.delete(); if (dmSent && dmMessage) await dmMessage.delete();
return; return;
} }
const failed = !dmd && notify; const failed = !dmSent && notify;
await interaction.editReply({ await interaction.editReply({
embeds: [ embeds: [
new EmojiEmbed() new EmojiEmbed()
@ -142,24 +179,25 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
}; };
const check = (interaction: CommandInteraction) => { const check = (interaction: CommandInteraction) => {
if (!interaction.guild) return;
const member = interaction.member as GuildMember; const member = interaction.member as GuildMember;
const me = interaction.guild!.me!; const me = interaction.guild.members.me!;
const apply = interaction.options.getMember("user") as GuildMember; const apply = interaction.options.getMember("user") as GuildMember;
const memberPos = member.roles.cache.size > 1 ? member.roles.highest.position : 0; 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 mePos = me.roles.cache.size > 1 ? me.roles.highest.position : 0;
const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0; const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0;
// Do not allow kicking the owner // 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) throw new Error("You cannot kick the owner of the server");
// Check if Nucleus can kick the member // 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)) throw new Error("I do not have a role higher than that member");
// Check if Nucleus has permission to kick // Check if Nucleus has permission to kick
if (!me.permissions.has("KICK_MEMBERS")) throw new Error("I do not have the *Kick Members* permission"); if (!me.permissions.has("KickMembers")) throw new Error("I do not have the *Kick Members* permission");
// Do not allow kicking Nucleus // Do not allow kicking Nucleus
if (member.id === interaction.guild!.me!.id) throw new Error("I cannot kick myself"); if (member.id === interaction.guild.members.me!.id) throw new Error("I cannot kick myself");
// Allow the owner to kick anyone // Allow the owner to kick anyone
if (member.id === interaction.guild!.ownerId) return true; if (member.id === interaction.guild.ownerId) return true;
// Check if the user has kick_members permission // Check if the user has kick_members permission
if (!member.permissions.has("KICK_MEMBERS")) throw new Error("You do not have the *Kick Members* permission"); if (!member.permissions.has("KickMembers")) throw new Error("You do not have the *Kick Members* permission");
// Check if the user is below on the role list // 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)) throw new Error("You do not have a role higher than that member");
// Allow kick // Allow kick

@ -49,6 +49,7 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
); );
const callback = async (interaction: CommandInteraction): Promise<unknown> => { const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (!interaction.guild) return;
const { log, NucleusColors, renderUser, entry, renderDelta } = client.logger; const { log, NucleusColors, renderUser, entry, renderDelta } = client.logger;
const user = interaction.options.getMember("user") as GuildMember; const user = interaction.options.getMember("user") as GuildMember;
const time = { const time = {
@ -57,7 +58,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
minutes: interaction.options.getInteger("minutes") ?? 0, minutes: interaction.options.getInteger("minutes") ?? 0,
seconds: interaction.options.getInteger("seconds") ?? 0 seconds: interaction.options.getInteger("seconds") ?? 0
}; };
const config = await client.database.guilds.read(interaction.guild!.id); const config = await client.database.guilds.read(interaction.guild.id);
let serverSettingsDescription = config.moderation.mute.timeout ? "given a timeout" : ""; let serverSettingsDescription = config.moderation.mute.timeout ? "given a timeout" : "";
if (config.moderation.mute.role) if (config.moderation.mute.role)
serverSettingsDescription += serverSettingsDescription +=
@ -187,9 +188,9 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.addCustomBoolean( .addCustomBoolean(
"appeal", "appeal",
"Create appeal ticket", "Create appeal ticket",
!(await areTicketsEnabled(interaction.guild!.id)), !(await areTicketsEnabled(interaction.guild.id)),
async () => async () =>
await create(interaction.guild!, interaction.options.getUser("user")!, interaction.user, reason), await create(interaction.guild, interaction.options.getUser("user")!, interaction.user, reason),
"An appeal ticket will be created when Confirm is clicked", "An appeal ticket will be created when Confirm is clicked",
"CONTROL.TICKET", "CONTROL.TICKET",
createAppealTicket createAppealTicket
@ -226,7 +227,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setEmoji("PUNISH.MUTE.RED") .setEmoji("PUNISH.MUTE.RED")
.setTitle("Muted") .setTitle("Muted")
.setDescription( .setDescription(
`You have been muted in ${interaction.guild!.name}` + `You have been muted in ${interaction.guild.name}` +
(reason (reason
? ` for:\n> ${reason}` ? ` for:\n> ${reason}`
: ".\n\n" + : ".\n\n" +
@ -267,7 +268,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (config.moderation.mute.role !== null) { if (config.moderation.mute.role !== null) {
await member.roles.add(config.moderation.mute.role); await member.roles.add(config.moderation.mute.role);
await client.database.eventScheduler.schedule("naturalUnmute", new Date().getTime() + muteTime * 1000, { await client.database.eventScheduler.schedule("naturalUnmute", new Date().getTime() + muteTime * 1000, {
guild: interaction.guild!.id, guild: interaction.guild.id,
user: user.id, user: user.id,
expires: new Date().getTime() + muteTime * 1000 expires: new Date().getTime() + muteTime * 1000
}); });
@ -280,7 +281,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (config.moderation.mute.role !== null) { if (config.moderation.mute.role !== null) {
await member.roles.add(config.moderation.mute.role); await member.roles.add(config.moderation.mute.role);
await client.database.eventScheduler.schedule("unmuteRole", new Date().getTime() + muteTime * 1000, { await client.database.eventScheduler.schedule("unmuteRole", new Date().getTime() + muteTime * 1000, {
guild: interaction.guild!.id, guild: interaction.guild.id,
user: user.id, user: user.id,
role: config.moderation.mute.role role: config.moderation.mute.role
}); });
@ -303,7 +304,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (dmd && dm) await dm.delete(); if (dmd && dm) await dm.delete();
return; return;
} }
await client.database.history.create("mute", interaction.guild!.id, member.user, interaction.user, reason); await client.database.history.create("mute", interaction.guild.id, member.user, interaction.user, reason);
const failed = !dmd && notify; const failed = !dmd && notify;
await interaction.editReply({ await interaction.editReply({
embeds: [ embeds: [
@ -342,7 +343,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
reason: entry(reason, reason ? reason : "*No reason provided*") reason: entry(reason, reason ? reason : "*No reason provided*")
}, },
hidden: { hidden: {
guild: interaction.guild!.id guild: interaction.guild.id
} }
}; };
log(data); log(data);
@ -361,14 +362,15 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
}; };
const check = (interaction: CommandInteraction) => { const check = (interaction: CommandInteraction) => {
if (!interaction.guild) return;
const member = interaction.member as GuildMember; const member = interaction.member as GuildMember;
const me = interaction.guild!.me!; const me = interaction.guild.me!;
const apply = interaction.options.getMember("user") as GuildMember; const apply = interaction.options.getMember("user") as GuildMember;
const memberPos = member.roles.cache.size > 1 ? member.roles.highest.position : 0; 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 mePos = me.roles.cache.size > 1 ? me.roles.highest.position : 0;
const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0; const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0;
// Do not allow muting the owner // 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) throw new Error("You cannot mute the owner of the server");
// Check if Nucleus can mute the member // 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)) throw new Error("I do not have a role higher than that member");
// Check if Nucleus has permission to mute // Check if Nucleus has permission to mute
@ -376,7 +378,7 @@ const check = (interaction: CommandInteraction) => {
// Do not allow muting Nucleus // Do not allow muting Nucleus
if (member.id === me.id) throw new Error("I cannot mute myself"); if (member.id === me.id) throw new Error("I cannot mute myself");
// Allow the owner to mute anyone // Allow the owner to mute anyone
if (member.id === interaction.guild!.ownerId) return true; if (member.id === interaction.guild.ownerId) return true;
// Check if the user has moderate_members permission // Check if the user has moderate_members permission
if (!member.permissions.has("MODERATE_MEMBERS")) if (!member.permissions.has("MODERATE_MEMBERS"))
throw new Error("You do not have the *Moderate Members* permission"); throw new Error("You do not have the *Moderate Members* permission");

@ -26,7 +26,7 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
); );
const callback = async (interaction: CommandInteraction): Promise<unknown> => { const callback = async (interaction: CommandInteraction): Promise<unknown> => {
const user = (interaction.options.getMember("user") as GuildMember) ?? null; const user = (interaction.options.getMember("user") as GuildMember);
const channel = interaction.channel as GuildChannel; const channel = interaction.channel as GuildChannel;
if ( if (
!["GUILD_TEXT", "GUILD_NEWS", "GUILD_NEWS_THREAD", "GUILD_PUBLIC_THREAD", "GUILD_PRIVATE_THREAD"].includes( !["GUILD_TEXT", "GUILD_NEWS", "GUILD_NEWS_THREAD", "GUILD_PUBLIC_THREAD", "GUILD_PRIVATE_THREAD"].includes(

@ -34,7 +34,7 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
); );
const callback = async (interaction: CommandInteraction): Promise<void> => { const callback = async (interaction: CommandInteraction): Promise<void> => {
let time = parseInt(interaction.options.getString("time") ?? "0"); let time = parseInt(interaction.options.get("time")?.value as string || "0");
if (time === 0 && (interaction.channel as TextChannel).rateLimitPerUser === 0) { if (time === 0 && (interaction.channel as TextChannel).rateLimitPerUser === 0) {
time = 10; time = 10;
} }
@ -91,10 +91,9 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
const check = (interaction: CommandInteraction) => { const check = (interaction: CommandInteraction) => {
const member = interaction.member as GuildMember; const member = interaction.member as GuildMember;
// Check if Nucleus can set the slowmode // Check if Nucleus can set the slowmode
if (!interaction.guild.me.permissions.has("MANAGE_CHANNELS")) if (!interaction.guild!.members.me!.permissions.has("ManageChannels")) throw new Error("I do not have the *Manage Channels* permission");
throw new Error("I do not have the *Manage Channels* permission");
// Check if the user has manage_channel permission // Check if the user has manage_channel permission
if (!member.permissions.has("MANAGE_CHANNELS")) throw new Error("You do not have the *Manage Channels* permission"); if (!member.permissions.has("ManageChannels")) throw new Error("You do not have the *Manage Channels* permission");
// Allow slowmode // Allow slowmode
return true; return true;
}; };

@ -1,5 +1,5 @@
import { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; import { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import confirmationMessage from "../../utils/confirmationMessage.js"; import confirmationMessage from "../../utils/confirmationMessage.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import keyValueList from "../../utils/generateKeyValueList.js"; import keyValueList from "../../utils/generateKeyValueList.js";

@ -14,6 +14,7 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
); );
const callback = async (interaction: CommandInteraction): Promise<unknown> => { const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (!interaction.guild) return;
const bans = await interaction.guild.bans.fetch(); const bans = await interaction.guild.bans.fetch();
const user = interaction.options.getString("user"); const user = interaction.options.getString("user");
let resolved = bans.find((ban) => ban.user.id === user); let resolved = bans.find((ban) => ban.user.id === user);
@ -107,6 +108,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
}; };
const check = (interaction: CommandInteraction) => { const check = (interaction: CommandInteraction) => {
if (!interaction.guild) return;
const member = interaction.member as GuildMember; const member = interaction.member as GuildMember;
const me = interaction.guild.me!; const me = interaction.guild.me!;
// Check if Nucleus can unban members // Check if Nucleus can unban members

@ -1,5 +1,5 @@
import { CommandInteraction, GuildMember } from "discord.js"; import type { CommandInteraction, GuildMember } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import type { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import confirmationMessage from "../../utils/confirmationMessage.js"; import confirmationMessage from "../../utils/confirmationMessage.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import keyValueList from "../../utils/generateKeyValueList.js"; import keyValueList from "../../utils/generateKeyValueList.js";
@ -12,40 +12,51 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.addUserOption((option) => option.setName("user").setDescription("The user to unmute").setRequired(true)); .addUserOption((option) => option.setName("user").setDescription("The user to unmute").setRequired(true));
const callback = async (interaction: CommandInteraction): Promise<unknown> => { const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (!interaction.guild) return;
const { log, NucleusColors, renderUser, entry, renderDelta } = client.logger; const { log, NucleusColors, renderUser, entry, renderDelta } = client.logger;
// TODO:[Modals] Replace this with a modal // TODO:[Modals] Replace this with a modal
let reason = null; let reason: string | null = null;
let notify = false; let notify = false;
let confirmation; let confirmation;
let timedOut = false;
let success = false; let success = false;
while (!success) { do {
confirmation = await new confirmationMessage(interaction) confirmation = await new confirmationMessage(interaction)
.setEmoji("PUNISH.MUTE.RED") .setEmoji("PUNISH.MUTE.RED")
.setTitle("Unmute") .setTitle("Unmute")
.setDescription( .setDescription(
keyValueList({ keyValueList({
user: renderUser(interaction.options.getUser("user")), user: renderUser(interaction.options.getUser("user")!),
reason: `\n> ${reason ? reason : "*No reason provided*"}` reason: `\n> ${reason ? reason : "*No reason provided*"}`
}) + }) +
`The user **will${notify ? "" : " not"}** be notified\n\n` +
`Are you sure you want to unmute <@!${(interaction.options.getMember("user") as GuildMember).id}>?` `Are you sure you want to unmute <@!${(interaction.options.getMember("user") as GuildMember).id}>?`
) )
.setColor("Danger") .setColor("Danger")
.addCustomBoolean(
"notify",
"Notify user",
false,
null,
"The user will be sent a DM",
"ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
notify
)
.addReasonButton(reason ?? "") .addReasonButton(reason ?? "")
.send(reason !== null); .send(reason !== null);
if (confirmation.success) success = true; if (confirmation.cancelled) timedOut = true;
else if (confirmation.success !== undefined) success = true;
else if (confirmation.newReason) reason = confirmation.newReason; else if (confirmation.newReason) reason = confirmation.newReason;
else if (confirmation.components) { else if (confirmation.components) {
notify = confirmation.components.notify.active; notify = confirmation.components!["notify"]!.active;
} }
} } while (!timedOut && !success);
if (confirmation.cancelled) return; if (confirmation.cancelled) return;
if (confirmation.success) { if (confirmation.success) {
let dmd = false; let dmSent = false;
let dm; let dmMessage;
try { try {
if (notify) { if (notify) {
dm = await (interaction.options.getMember("user") as GuildMember).send({ dmMessage = await (interaction.options.getMember("user") as GuildMember).send({
embeds: [ embeds: [
new EmojiEmbed() new EmojiEmbed()
.setEmoji("PUNISH.MUTE.GREEN") .setEmoji("PUNISH.MUTE.GREEN")
@ -57,10 +68,10 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setStatus("Success") .setStatus("Success")
] ]
}); });
dmd = true; dmSent = true;
} }
} catch { } catch {
dmd = false; dmSent = false;
} }
const member = interaction.options.getMember("user") as GuildMember; const member = interaction.options.getMember("user") as GuildMember;
try { try {
@ -76,7 +87,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
], ],
components: [] components: []
}); });
if (dmd) await dm.delete(); if (dmSent && dmMessage) await dmMessage.delete();
return; return;
} }
await client.database.history.create( await client.database.history.create(
@ -98,7 +109,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
list: { list: {
memberId: entry(member.user.id, `\`${member.user.id}\``), memberId: entry(member.user.id, `\`${member.user.id}\``),
name: entry(member.user.id, renderUser(member.user)), name: entry(member.user.id, renderUser(member.user)),
unmuted: entry(new Date().getTime(), renderDelta(new Date().getTime())), unmuted: entry(new Date().getTime().toString(), renderDelta(new Date().getTime())),
unmutedBy: entry(interaction.user.id, renderUser(interaction.user)) unmutedBy: entry(interaction.user.id, renderUser(interaction.user))
}, },
hidden: { hidden: {
@ -106,7 +117,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
} }
}; };
log(data); log(data);
const failed = !dmd && notify; const failed = !dmSent && notify;
await interaction.editReply({ await interaction.editReply({
embeds: [ embeds: [
new EmojiEmbed() new EmojiEmbed()
@ -132,10 +143,10 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
}; };
const check = (interaction: CommandInteraction) => { const check = (interaction: CommandInteraction) => {
if (!interaction.guild) return;
const member = interaction.member as GuildMember; const member = interaction.member as GuildMember;
const me = interaction.guild.me!; const me = interaction.guild.members.me!;
const apply = interaction.options.getMember("user") as GuildMember; 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 memberPos = member.roles.cache.size > 1 ? member.roles.highest.position : 0; 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 mePos = me.roles.cache.size > 1 ? me.roles.highest.position : 0;
const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0; const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0;
@ -144,11 +155,11 @@ const check = (interaction: CommandInteraction) => {
// Check if Nucleus can unmute the member // 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)) throw new Error("I do not have a role higher than that member");
// Check if Nucleus has permission to unmute // Check if Nucleus has permission to unmute
if (!me.permissions.has("MODERATE_MEMBERS")) throw new Error("I do not have the *Moderate Members* permission"); if (!me.permissions.has("ModerateMembers")) throw new Error("I do not have the *Moderate Members* permission");
// Allow the owner to unmute anyone // Allow the owner to unmute anyone
if (member.id === interaction.guild.ownerId) return true; if (member.id === interaction.guild.ownerId) return true;
// Check if the user has moderate_members permission // Check if the user has moderate_members permission
if (!member.permissions.has("MODERATE_MEMBERS")) if (!member.permissions.has("ModerateMembers"))
throw new Error("You do not have the *Moderate Members* permission"); throw new Error("You do not have the *Moderate Members* permission");
// Check if the user is below on the role list // 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)) throw new Error("You do not have a role higher than that member");

@ -14,6 +14,7 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.addUserOption((option) => option.setName("user").setDescription("The user to warn").setRequired(true)); .addUserOption((option) => option.setName("user").setDescription("The user to warn").setRequired(true));
const callback = async (interaction: CommandInteraction): Promise<unknown> => { const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (interaction.guild === null) return;
const { log, NucleusColors, renderUser, entry } = client.logger; const { log, NucleusColors, renderUser, entry } = client.logger;
// TODO:[Modals] Replace this with a modal // TODO:[Modals] Replace this with a modal
let reason: string | null = null; let reason: string | null = null;
@ -37,7 +38,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.addCustomBoolean( .addCustomBoolean(
"appeal", "appeal",
"Create appeal ticket", "Create appeal ticket",
!(await areTicketsEnabled(interaction.guild!.id)), !(await areTicketsEnabled(interaction.guild.id)),
async () => await create(interaction.guild!, interaction.options.getUser("user")!, interaction.user, reason), async () => await create(interaction.guild!, interaction.options.getUser("user")!, interaction.user, reason),
"An appeal ticket will be created", "An appeal ticket will be created",
"CONTROL.TICKET", "CONTROL.TICKET",
@ -64,79 +65,166 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
} }
} while (!timedOut && !success) } while (!timedOut && !success)
if (timedOut) return; if (timedOut) return;
if (confirmation.success) { if (!confirmation.success) {
let dmSent = false; await interaction.editReply({
const config = await client.database.guilds.read(interaction.guild!.id); embeds: [
try { new EmojiEmbed()
if (notify) { .setEmoji("PUNISH.WARN.GREEN")
const messageData: { .setTitle("Warn")
embeds: EmojiEmbed[]; .setDescription("No changes were made")
components: ActionRowBuilder<ButtonBuilder>[]; .setStatus("Success")
} = { ],
embeds: [ components: []
new EmojiEmbed() });
.setEmoji("PUNISH.WARN.RED") return;
.setTitle("Warned") }
.setDescription( let dmSent = false;
`You have been warned in ${interaction.guild!.name}` + const config = await client.database.guilds.read(interaction.guild.id);
(reason ? ` for:\n> ${reason}` : ".\n*No reason was provided*") + try {
"\n\n" + if (notify) {
(createAppealTicket const messageData: {
? `You can appeal this in the ticket created in <#${confirmation.components!["appeal"]!.response}>` embeds: EmojiEmbed[];
: "") components: ActionRowBuilder<ButtonBuilder>[];
) } = {
.setStatus("Danger") embeds: [
], new EmojiEmbed()
components: [] .setEmoji("PUNISH.WARN.RED")
}; .setTitle("Warned")
if (config.moderation.warn.text && config.moderation.warn.link) { .setDescription(
messageData.embeds[0]!.setFooter(LinkWarningFooter) `You have been warned in ${interaction.guild.name}` +
messageData.components.push(new ActionRowBuilder<Discord.ButtonBuilder>() (reason ? ` for:\n> ${reason}` : ".\n*No reason was provided*") +
.addComponents(new ButtonBuilder() "\n\n" +
.setStyle(ButtonStyle.Link) (createAppealTicket
.setLabel(config.moderation.warn.text) ? `You can appeal this in the ticket created in <#${confirmation.components!["appeal"]!.response}>`
.setURL(config.moderation.warn.link) : "")
) )
) .setStatus("Danger")
} ],
await (interaction.options.getMember("user") as GuildMember).send(messageData); components: []
dmSent = true; };
if (config.moderation.warn.text && config.moderation.warn.link) {
messageData.embeds[0]!.setFooter(LinkWarningFooter)
messageData.components.push(new ActionRowBuilder<Discord.ButtonBuilder>()
.addComponents(new ButtonBuilder()
.setStyle(ButtonStyle.Link)
.setLabel(config.moderation.warn.text)
.setURL(config.moderation.warn.link)
)
)
} }
await (interaction.options.getMember("user") as GuildMember).send(messageData);
dmSent = true;
}
} catch (e) {
dmSent = false;
}
const data = {
meta: {
type: "memberWarn",
displayName: "Member warned",
calculateType: "guildMemberPunish",
color: NucleusColors.yellow,
emoji: "PUNISH.WARN.YELLOW",
timestamp: new Date().getTime()
},
list: {
user: entry(
(interaction.options.getMember("user") as GuildMember).user.id,
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*"
},
hidden: {
guild: interaction.guild.id
}
};
await client.database.history.create(
"warn",
interaction.guild.id,
(interaction.options.getMember("user") as GuildMember).user,
interaction.user,
reason
);
log(data);
const failed = !dmSent && notify;
if (!failed) {
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("PUNISH.WARN.GREEN")
.setTitle("Warn")
.setDescription(
"The user was warned" +
(createAppealTicket
? ` and an appeal ticket was opened in <#${confirmation.components!["appeal"]!.response}>`
: "")
)
.setStatus("Success")
],
components: []
});
} else {
const canSeeChannel = (interaction.options.getMember("user") as GuildMember)
.permissionsIn(interaction.channel as Discord.TextChannel)
.has("ViewChannel");
const m = (await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("PUNISH.WARN.RED")
.setTitle("Warn")
.setDescription("The user's DMs are not open\n\nWhat would you like to do?")
.setStatus("Danger")
],
components: [
new ActionRowBuilder<Discord.ButtonBuilder>().addComponents(
new Discord.ButtonBuilder().setCustomId("log").setLabel("Ignore and log").setStyle(ButtonStyle.Secondary),
new Discord.ButtonBuilder()
.setCustomId("here")
.setLabel("Warn here")
.setStyle(canSeeChannel ? ButtonStyle.Primary : ButtonStyle.Secondary)
.setDisabled(!canSeeChannel),
new Discord.ButtonBuilder()
.setCustomId("ticket")
.setLabel("Create ticket")
.setStyle(canSeeChannel ? ButtonStyle.Primary : ButtonStyle.Secondary)
.setDisabled(createAppealTicket)
)
]
})) as Discord.Message;
let component;
try {
component = await m.awaitMessageComponent({
filter: (m) => m.user.id === interaction.user.id,
time: 300000
});
} catch (e) { } catch (e) {
dmSent = false; return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("PUNISH.WARN.GREEN")
.setTitle("Warn")
.setDescription("No changes were made")
.setStatus("Success")
],
components: []
});
} }
const data = { if (component.customId === "here") {
meta: { await interaction.channel!.send({
type: "memberWarn", embeds: [
displayName: "Member warned", new EmojiEmbed()
calculateType: "guildMemberPunish", .setEmoji("PUNISH.WARN.RED")
color: NucleusColors.yellow, .setTitle("Warn")
emoji: "PUNISH.WARN.YELLOW", .setDescription("You have been warned" + (reason ? ` for:\n> ${reason}` : "."))
timestamp: new Date().getTime() .setStatus("Danger")
}, ],
list: { content: `<@!${(interaction.options.getMember("user") as GuildMember).id}>`,
user: entry( allowedMentions: {
(interaction.options.getMember("user") as GuildMember).user.id, users: [(interaction.options.getMember("user") as GuildMember).id]
renderUser((interaction.options.getMember("user") as GuildMember).user) }
), });
warnedBy: entry(interaction.member!.user.id, renderUser(interaction.member!.user as Discord.User)), return await interaction.editReply({
reason: reason ? `\n> ${reason}` : "*No reason provided*"
},
hidden: {
guild: interaction.guild!.id
}
};
await client.database.history.create(
"warn",
interaction.guild!.id,
(interaction.options.getMember("user") as GuildMember).user,
interaction.user,
reason
);
log(data);
const failed = !dmSent && notify;
if (!failed) {
await interaction.editReply({
embeds: [ embeds: [
new EmojiEmbed() new EmojiEmbed()
.setEmoji("PUNISH.WARN.GREEN") .setEmoji("PUNISH.WARN.GREEN")
@ -151,139 +239,53 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
], ],
components: [] components: []
}); });
} else { } else if (component.customId === "log") {
const canSeeChannel = (interaction.options.getMember("user") as GuildMember) await interaction.editReply({
.permissionsIn(interaction.channel as Discord.TextChannel)
.has("ViewChannel");
const m = (await interaction.editReply({
embeds: [ embeds: [
new EmojiEmbed() new EmojiEmbed()
.setEmoji("PUNISH.WARN.RED") .setEmoji("PUNISH.WARN.GREEN")
.setTitle("Warn") .setTitle("Warn")
.setDescription("The user's DMs are not open\n\nWhat would you like to do?") .setDescription("The warn was logged")
.setStatus("Danger") .setStatus("Success")
], ],
components: [ components: []
new ActionRowBuilder<Discord.ButtonBuilder>().addComponents( });
new Discord.ButtonBuilder().setCustomId("log").setLabel("Ignore and log").setStyle(ButtonStyle.Secondary), } else if (component.customId === "ticket") {
new Discord.ButtonBuilder() const ticketChannel = await create(
.setCustomId("here") interaction.guild,
.setLabel("Warn here") interaction.options.getUser("user")!,
.setStyle(canSeeChannel ? ButtonStyle.Primary : ButtonStyle.Secondary) interaction.user,
.setDisabled(!canSeeChannel), reason,
new Discord.ButtonBuilder() "Warn Notification"
.setCustomId("ticket") );
.setLabel("Create ticket") if (ticketChannel === null) {
.setStyle(canSeeChannel ? ButtonStyle.Primary : ButtonStyle.Secondary)
.setDisabled(createAppealTicket)
)
]
})) as Discord.Message;
let component;
try {
component = await m.awaitMessageComponent({
filter: (m) => m.user.id === interaction.user.id,
time: 300000
});
} catch (e) {
return await interaction.editReply({ return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("PUNISH.WARN.GREEN")
.setTitle("Warn")
.setDescription("No changes were made")
.setStatus("Success")
],
components: []
});
}
if (component.customId === "here") {
await interaction.channel!.send({
embeds: [ embeds: [
new EmojiEmbed() new EmojiEmbed()
.setEmoji("PUNISH.WARN.RED") .setEmoji("PUNISH.WARN.RED")
.setTitle("Warn") .setTitle("Warn")
.setDescription("You have been warned" + (reason ? ` for:\n> ${reason}` : ".")) .setDescription("A ticket could not be created")
.setStatus("Danger") .setStatus("Danger")
], ],
content: `<@!${(interaction.options.getMember("user") as GuildMember).id}>`,
allowedMentions: {
users: [(interaction.options.getMember("user") as GuildMember).id]
}
});
return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("PUNISH.WARN.GREEN")
.setTitle("Warn")
.setDescription(
"The user was warned" +
(createAppealTicket
? ` and an appeal ticket was opened in <#${confirmation.components!["appeal"]!.response}>`
: "")
)
.setStatus("Success")
],
components: []
});
} else if (component.customId === "log") {
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("PUNISH.WARN.GREEN")
.setTitle("Warn")
.setDescription("The warn was logged")
.setStatus("Success")
],
components: []
});
} else if (component.customId === "ticket") {
const ticketChannel = await create(
interaction.guild!,
interaction.options.getUser("user")!,
interaction.user,
reason,
"Warn Notification"
);
if (ticketChannel === null) {
return await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("PUNISH.WARN.RED")
.setTitle("Warn")
.setDescription("A ticket could not be created")
.setStatus("Danger")
],
components: []
});
}
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("PUNISH.WARN.GREEN")
.setTitle("Warn")
.setDescription(`A ticket was created in <#${ticketChannel}>`)
.setStatus("Success")
],
components: [] components: []
}); });
} }
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("PUNISH.WARN.GREEN")
.setTitle("Warn")
.setDescription(`A ticket was created in <#${ticketChannel}>`)
.setStatus("Success")
],
components: []
});
} }
} else {
await interaction.editReply({
embeds: [
new EmojiEmbed()
.setEmoji("PUNISH.WARN.GREEN")
.setTitle("Warn")
.setDescription("No changes were made")
.setStatus("Success")
],
components: []
});
} }
}; };
const check = (interaction: CommandInteraction) => { const check = (interaction: CommandInteraction) => {
if (!interaction.guild) return;
const member = interaction.member as GuildMember; const member = interaction.member as GuildMember;
const apply = interaction.options.getMember("user") as GuildMember | null; 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) throw new Error("That member is not in the server");
@ -292,7 +294,7 @@ const check = (interaction: CommandInteraction) => {
// Do not allow warning bots // Do not allow warning bots
if (member.user.bot) throw new Error("I cannot warn bots"); if (member.user.bot) throw new Error("I cannot warn bots");
// Allow the owner to warn anyone // Allow the owner to warn anyone
if (member.id === interaction.guild!.ownerId) return true; if (member.id === interaction.guild.ownerId) return true;
// Check if the user has moderate_members permission // Check if the user has moderate_members permission
if (!member.permissions.has("ModerateMembers")) if (!member.permissions.has("ModerateMembers"))
throw new Error("You do not have the *Moderate Members* permission"); throw new Error("You do not have the *Moderate Members* permission");

@ -62,6 +62,7 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
); );
const callback = async (interaction: CommandInteraction): Promise<unknown> => { const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (!interaction.guild) return;
let m = (await interaction.reply({ let m = (await interaction.reply({
embeds: LoadingEmbed, embeds: LoadingEmbed,
ephemeral: true, ephemeral: true,
@ -77,7 +78,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (options.category) { if (options.category) {
let channel: GuildChannel | null; let channel: GuildChannel | null;
try { try {
channel = await interaction.guild!.channels.fetch(options.category.id); channel = await interaction.guild.channels.fetch(options.category.id);
} catch { } catch {
return await interaction.editReply({ return await interaction.editReply({
embeds: [ embeds: [
@ -91,7 +92,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
} }
if (!channel) return; if (!channel) return;
channel = channel as Discord.CategoryChannel; channel = channel as Discord.CategoryChannel;
if (channel.guild.id !== interaction.guild!.id) if (channel.guild.id !== interaction.guild.id)
return interaction.editReply({ return interaction.editReply({
embeds: [ embeds: [
new EmojiEmbed() new EmojiEmbed()
@ -117,7 +118,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
let role: Role | null; let role: Role | null;
if (options.supportping) { if (options.supportping) {
try { try {
role = await interaction.guild!.roles.fetch(options.supportping.id); role = await interaction.guild.roles.fetch(options.supportping.id);
} catch { } catch {
return await interaction.editReply({ return await interaction.editReply({
embeds: [ embeds: [
@ -131,7 +132,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
} }
if (!role) return; if (!role) return;
role = role as Discord.Role; role = role as Discord.Role;
if (role.guild.id !== interaction.guild!.id) if (role.guild.id !== interaction.guild.id)
return interaction.editReply({ return interaction.editReply({
embeds: [ embeds: [
new EmojiEmbed() new EmojiEmbed()
@ -170,7 +171,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (options.maxtickets) toUpdate["tickets.maxTickets"] = options.maxtickets; if (options.maxtickets) toUpdate["tickets.maxTickets"] = options.maxtickets;
if (options.supportping) toUpdate["tickets.supportRole"] = options.supportping.id; if (options.supportping) toUpdate["tickets.supportRole"] = options.supportping.id;
try { try {
await client.database.guilds.write(interaction.guild!.id, toUpdate); await client.database.guilds.write(interaction.guild.id, toUpdate);
} catch (e) { } catch (e) {
return interaction.editReply({ return interaction.editReply({
embeds: [ embeds: [
@ -196,7 +197,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
}); });
} }
} }
let data = await client.database.guilds.read(interaction.guild!.id); let data = await client.database.guilds.read(interaction.guild.id);
data.tickets.customTypes = (data.tickets.customTypes || []).filter( data.tickets.customTypes = (data.tickets.customTypes || []).filter(
(value: string, index: number, array: string[]) => array.indexOf(value) === index (value: string, index: number, array: string[]) => array.indexOf(value) === index
); );
@ -284,19 +285,19 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if ((i.component as Component).customId === "clearCategory") { if ((i.component as Component).customId === "clearCategory") {
if (lastClicked === "cat") { if (lastClicked === "cat") {
lastClicked = ""; lastClicked = "";
await client.database.guilds.write(interaction.guild!.id, null, ["tickets.category"]); await client.database.guilds.write(interaction.guild.id, null, ["tickets.category"]);
data.category = undefined; data.category = undefined;
} else lastClicked = "cat"; } else lastClicked = "cat";
} else if ((i.component as Component).customId === "clearMaxTickets") { } else if ((i.component as Component).customId === "clearMaxTickets") {
if (lastClicked === "max") { if (lastClicked === "max") {
lastClicked = ""; lastClicked = "";
await client.database.guilds.write(interaction.guild!.id, null, ["tickets.maxTickets"]); await client.database.guilds.write(interaction.guild.id, null, ["tickets.maxTickets"]);
data.maxTickets = 5; data.maxTickets = 5;
} else lastClicked = "max"; } else lastClicked = "max";
} else if ((i.component as Component).customId === "clearSupportPing") { } else if ((i.component as Component).customId === "clearSupportPing") {
if (lastClicked === "sup") { if (lastClicked === "sup") {
lastClicked = ""; lastClicked = "";
await client.database.guilds.write(interaction.guild!.id, null, ["tickets.supportRole"]); await client.database.guilds.write(interaction.guild.id, null, ["tickets.supportRole"]);
data.supportRole = undefined; data.supportRole = undefined;
} else lastClicked = "sup"; } else lastClicked = "sup";
} else if ((i.component as Component).customId === "send") { } else if ((i.component as Component).customId === "send") {
@ -732,7 +733,7 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t
const check = (interaction: CommandInteraction) => { const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember; const member = interaction.member as Discord.GuildMember;
if (!member.permissions.has("MANAGE_GUILD")) if (!member.permissions.has("ManageGuild"))
throw new Error("You must have the *Manage Server* permission to use this command"); throw new Error("You must have the *Manage Server* permission to use this command");
return true; return true;
}; };

@ -30,6 +30,7 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
); );
const callback = async (interaction: CommandInteraction): Promise<unknown> => { const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (!interaction.guild) return;
const m = (await interaction.reply({ const m = (await interaction.reply({
embeds: LoadingEmbed, embeds: LoadingEmbed,
ephemeral: true, ephemeral: true,
@ -51,7 +52,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
}); });
} }
role = role as Discord.Role; role = role as Discord.Role;
if (role.guild.id !== interaction.guild!.id) { if (role.guild.id !== interaction.guild.id) {
return interaction.editReply({ return interaction.editReply({
embeds: [ embeds: [
new EmojiEmbed() new EmojiEmbed()
@ -72,7 +73,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
if (confirmation.cancelled) return; if (confirmation.cancelled) return;
if (confirmation.success) { if (confirmation.success) {
try { try {
await client.database.guilds.write(interaction.guild!.id, { await client.database.guilds.write(interaction.guild.id, {
"verify.role": role.id, "verify.role": role.id,
"verify.enabled": true "verify.enabled": true
}); });
@ -92,7 +93,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
role: entry(role.id, renderRole(role)) role: entry(role.id, renderRole(role))
}, },
hidden: { hidden: {
guild: interaction.guild!.id guild: interaction.guild.id
} }
}; };
log(data); log(data);
@ -123,7 +124,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
} }
} }
let clicks = 0; let clicks = 0;
const data = await client.database.guilds.read(interaction.guild!.id); const data = await client.database.guilds.read(interaction.guild.id);
let role = data.verify.role; let role = data.verify.role;
let timedOut = false; let timedOut = false;
@ -166,7 +167,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
clicks += 1; clicks += 1;
if (clicks === 2) { if (clicks === 2) {
clicks = 0; clicks = 0;
await client.database.guilds.write(interaction.guild!.id, null, ["verify.role", "verify.enabled"]); await client.database.guilds.write(interaction.guild.id, null, ["verify.role", "verify.enabled"]);
role = undefined; role = undefined;
} }
} else if ((i.component as Component).customId === "send") { } else if ((i.component as Component).customId === "send") {
@ -379,7 +380,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
const check = (interaction: CommandInteraction) => { const check = (interaction: CommandInteraction) => {
const member = interaction.member as Discord.GuildMember; const member = interaction.member as Discord.GuildMember;
if (!member.permissions.has("MANAGE_GUILD")) if (!member.permissions.has("ManageGuild"))
throw new Error("You must have the *Manage Server* permission to use this command"); throw new Error("You must have the *Manage Server* permission to use this command");
return true; return true;
}; };

@ -11,6 +11,8 @@ const defaultDict: Record<string, string | string[] | boolean> = {
owners: [], owners: [],
commandsFolder: "Your built commands folder (usually dist/commands)", commandsFolder: "Your built commands folder (usually dist/commands)",
eventsFolder: "Your built events folder (usually dist/events)", eventsFolder: "Your built events folder (usually dist/events)",
messageContextFolder: "Your built message context folder (usually dist/context/messages)",
userContextFolder: "Your built user context folder (usually dist/context/users)",
verifySecret: verifySecret:
"If using verify, enter a code here which matches the secret sent back by your website. You can use a random code if you do not have one already. (Optional)", "If using verify, enter a code here which matches the secret sent back by your website. You can use a random code if you do not have one already. (Optional)",
mongoUrl: "Your Mongo connection string, e.g. mongodb://127.0.0.1:27017", mongoUrl: "Your Mongo connection string, e.g. mongodb://127.0.0.1:27017",

@ -1,4 +1,5 @@
import { Interaction, SlashCommandBuilder } from 'discord.js'; import { typeSlashCommandSubcommandBuilder } from '@discordjs/builders';
import { Interaction, SlashCommandBuilder, ApplicationCommandType } from 'discord.js';
// @ts-expect-error // @ts-expect-error
import config from "../../config/main.json" assert { type: "json" }; import config from "../../config/main.json" assert { type: "json" };
import client from "../client.js"; import client from "../client.js";
@ -88,8 +89,64 @@ async function registerEvents() {
console.log(`Loaded ${files.length - errors} events (${errors} failed)`) console.log(`Loaded ${files.length - errors} events (${errors} failed)`)
}; };
async function registerContextMenus() {
console.log("Reading context menus")
const messageFiles = fs.readdirSync(config.messageContextFolder, { withFileTypes: true }).filter(
file => !file.name.endsWith(".ts") && !file.name.endsWith(".map")
);
const userFiles = fs.readdirSync(config.userContextFolder, { withFileTypes: true }).filter(
file => !file.name.endsWith(".ts") && !file.name.endsWith(".map")
);
console.log(`Registering ${messageFiles.length} message context menus and ${userFiles.length} user context menus`)
let i = 0;
let errors = 0;
for (const file of messageFiles) {
const last = i === messageFiles.length - 1 ? "└" : "├";
i++;
try {
console.log(`${last}${colours.yellow}Loading message context menu ${file.name}${colours.none}`)
const context = (await import(`../../../${config.messageContextFolder}/${file.name}`));
context.command.setType(ApplicationCommandType.Message);
client.commands["contextCommands/message/" + file.name.replace(".js", "")] = context;
console.log(`${last.replace("└", " ").replace("├", "│")} └─ ${colours.green}Loaded ${file.name} [${i} / ${messageFiles.length}]${colours.none}`)
} catch (e) {
errors++;
console.log(`${last.replace("└", " ").replace("├", "│")} └─ ${colours.red}Failed to load ${file.name} [${i} / ${messageFiles.length}]${colours.none}`)
}
}
for (const file of userFiles) {
const last = i === userFiles.length - 1 ? "└" : "├";
i++;
try {
console.log(`${last}${colours.yellow}Loading user context menu ${file.name}${colours.none}`)
const context = (await import(`../../../${config.userContextFolder}/${file.name}`));
context.command.setType(ApplicationCommandType.User);
client.commands["contextCommands/user/" + file.name.replace(".js", "")] = context;
console.log(`${last.replace("└", " ").replace("├", "│")} └─ ${colours.green}Loaded ${file.name} [${i} / ${userFiles.length}]${colours.none}`)
} catch (e) {
errors++;
console.log(`${last.replace("└", " ").replace("├", "│")} └─ ${colours.red}Failed to load ${file.name} [${i} / ${userFiles.length}]${colours.none}`)
}
}
console.log(`Loaded ${messageFiles.length + userFiles.length - errors} context menus (${errors} failed)`)
};
async function registerCommandHandler() { async function registerCommandHandler() {
client.on("interactionCreate", async (interaction: Interaction) => { client.on("interactionCreate", async (interaction: Interaction) => {
if (interaction.isUserContextMenuCommand()) {;
const commandName = interaction.commandName;
console.log(commandName);
return;
} else if (interaction.isMessageContextMenuCommand()) {
const commandName = interaction.commandName;
console.log(commandName);
return;
}
if (!interaction.isChatInputCommand()) return; if (!interaction.isChatInputCommand()) return;
const commandName = interaction.commandName; const commandName = interaction.commandName;
@ -118,7 +175,8 @@ async function registerCommandHandler() {
} }
export default async function register() { export default async function register() {
await registerCommands(); let commandList: (SlashCommandBuilder | SlashCommandSubcommandBuilder)[] = []
commandList.concat(await registerCommands());
await registerCommandHandler(); await registerCommandHandler();
await registerEvents(); await registerEvents();
console.log(`${colours.green}Registered commands and events${colours.none}`) console.log(`${colours.green}Registered commands and events${colours.none}`)

Loading…
Cancel
Save