Please look over - not for production

pull/39/head
pineafan 3 years ago
parent f42285d930
commit 6de4da59ed
Signed by: Pinea
GPG Key ID: E5E1C2D43B0E4AB3

@ -3,9 +3,9 @@ import Discord, {
GuildMember, GuildMember,
ActionRowBuilder, ActionRowBuilder,
ButtonBuilder, ButtonBuilder,
User,
ButtonStyle, ButtonStyle,
SlashCommandSubcommandBuilder SlashCommandSubcommandBuilder,
ButtonInteraction
} from "discord.js"; } 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";
@ -29,8 +29,16 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.setRequired(false) .setRequired(false)
); );
const callback = async (interaction: CommandInteraction): Promise<void> => { const callback = async (interaction: CommandInteraction | ButtonInteraction, member?: GuildMember): Promise<void> => {
if (!interaction.guild) return; if (!interaction.guild) return;
let deleteDays;
if (!interaction.isButton()) {
member = interaction.options.getMember("user") as GuildMember;
deleteDays = (interaction.options.get("delete")?.value as number | null) ?? 0
} else {
deleteDays = 0;
}
if (!member) return;
const { renderUser } = client.logger; const { renderUser } = client.logger;
// TODO:[Modals] Replace the command arguments with a modal // TODO:[Modals] Replace the command arguments with a modal
let reason = null; let reason = null;
@ -44,15 +52,12 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
.setTitle("Ban") .setTitle("Ban")
.setDescription( .setDescription(
keyValueList({ keyValueList({
user: renderUser(interaction.options.getUser("user")!), user: renderUser(member.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` + `The user **will${notify ? "" : " not"}** be notified\n` +
`${addPlurals( `${addPlurals(deleteDays, "day")} of messages will be deleted\n\n` +
(interaction.options.get("delete")?.value as number | null) ?? 0, `Are you sure you want to ban <@!${member.id}>?`
"day"
)} of messages will be deleted\n\n` +
`Are you sure you want to ban <@!${(interaction.options.getMember("user") as GuildMember).id}>?`
) )
.addCustomBoolean( .addCustomBoolean(
"notify", "notify",
@ -113,23 +118,21 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
.setURL( .setURL(
config.moderation.ban.link.replaceAll( config.moderation.ban.link.replaceAll(
"{id}", "{id}",
(interaction.options.getMember("user") as GuildMember).id member.id
) )
) )
) )
); );
} }
dmMessage = await (interaction.options.getMember("user") as GuildMember).send(messageData); dmMessage = await member.send(messageData);
dmSent = true; dmSent = true;
} }
} catch { } catch {
dmSent = false; dmSent = false;
} }
try { try {
const member = interaction.options.getMember("user") as GuildMember;
const days: number = (interaction.options.get("delete")?.value as number | null) ?? 0;
member.ban({ member.ban({
deleteMessageSeconds: days * 24 * 60 * 60, deleteMessageSeconds: deleteDays * 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);
@ -189,23 +192,22 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
}); });
}; };
const check = async (interaction: CommandInteraction, partial: boolean = false) => { const check = (interaction: CommandInteraction | ButtonInteraction, partial: boolean = false, target?: GuildMember) => {
if (!interaction.guild) return; if (!interaction.guild) return;
const member = interaction.member as GuildMember; const member = interaction.member as GuildMember;
// Check if the user has ban_members permission // Check if the user has ban_members permission
if (!member.permissions.has("BanMembers")) return "You do not have the *Ban Members* permission"; if (!member.permissions.has("BanMembers")) return "You do not have the *Ban Members* permission";
if (partial) return true; if (partial) return true;
const me = interaction.guild.members.me!; const me = interaction.guild.members.me!;
let apply = interaction.options.getUser("user") as User | GuildMember; let apply: GuildMember;
if (interaction.isButton()) {
apply = target!;
} else {
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;
let applyPos = 0; const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0;
try {
apply = (await interaction.guild.members.fetch(apply.id)) as GuildMember;
applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0;
} catch {
apply = apply as User;
}
// Do not allow banning the owner // Do not allow banning the owner
if (member.id === interaction.guild.ownerId) return "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 // Check if Nucleus can ban the member

@ -5,7 +5,8 @@ import {
ActionRowBuilder, ActionRowBuilder,
ButtonBuilder, ButtonBuilder,
ButtonStyle, ButtonStyle,
SlashCommandSubcommandBuilder SlashCommandSubcommandBuilder,
ButtonInteraction
} from "discord.js"; } from "discord.js";
// @ts-expect-error // @ts-expect-error
import humanizeDuration from "humanize-duration"; import humanizeDuration from "humanize-duration";
@ -22,8 +23,12 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.setDescription("Kicks a user from the server") .setDescription("Kicks a user from the server")
.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 | ButtonInteraction, member?: GuildMember): Promise<void> => {
if (!interaction.guild) return; if (!interaction.guild) return;
if (!interaction.isButton()) {
member = interaction.options.getMember("user") as GuildMember;
}
if (!member) 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: string | null = null; let reason: string | null = null;
@ -37,9 +42,9 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setTitle("Kick") .setTitle("Kick")
.setDescription( .setDescription(
keyValueList({ keyValueList({
user: renderUser(interaction.options.getUser("user")!), user: renderUser(member.user),
reason: reason ? "\n> " + reason.replaceAll("\n", "\n> ") : "*No reason provided*" reason: reason ? "\n> " + reason.replaceAll("\n", "\n> ") : "*No reason provided*"
}) + `Are you sure you want to kick <@!${(interaction.options.getMember("user") as GuildMember).id}>?` }) + `Are you sure you want to kick <@!${member.id}>?`
) )
.setColor("Danger") .setColor("Danger")
.addCustomBoolean( .addCustomBoolean(
@ -101,21 +106,20 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setURL( .setURL(
config.moderation.kick.link.replaceAll( config.moderation.kick.link.replaceAll(
"{id}", "{id}",
(interaction.options.getMember("user") as GuildMember).id member.id
) )
) )
) )
); );
} }
dmMessage = await (interaction.options.getMember("user") as GuildMember).send(messageData); dmMessage = await member.send(messageData);
dmSent = true; dmSent = true;
} }
} catch { } catch {
dmSent = false; dmSent = false;
} }
try { try {
(interaction.options.getMember("user") as GuildMember).kick(reason || "No reason provided"); member.kick(reason || "No reason provided");
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 const timeInServer = member.joinedTimestamp
@ -186,7 +190,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
}); });
}; };
const check = (interaction: CommandInteraction, partial: boolean = false) => { const check = (interaction: CommandInteraction | ButtonInteraction, partial: boolean = false, target?: GuildMember) => {
if (!interaction.guild) return; if (!interaction.guild) return;
const member = interaction.member as GuildMember; const member = interaction.member as GuildMember;
@ -195,7 +199,12 @@ const check = (interaction: CommandInteraction, partial: boolean = false) => {
if (partial) return true; if (partial) return true;
const me = interaction.guild.members.me!; const me = interaction.guild.members.me!;
const apply = interaction.options.getMember("user") as GuildMember; let apply: GuildMember;
if (interaction.isButton()) {
apply = target!;
} else {
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;

@ -1,5 +1,5 @@
import { LinkWarningFooter, LoadingEmbed } from "../../utils/defaults.js"; import { LinkWarningFooter, LoadingEmbed } from "../../utils/defaults.js";
import Discord, { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; import Discord, { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle, ButtonInteraction } from "discord.js";
import type { SlashCommandSubcommandBuilder } from "discord.js"; import type { SlashCommandSubcommandBuilder } from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import getEmojiByName from "../../utils/getEmojiByName.js"; import getEmojiByName from "../../utils/getEmojiByName.js";
@ -49,16 +49,22 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.setRequired(false) .setRequired(false)
); );
const callback = async (interaction: CommandInteraction): Promise<unknown> => { const callback = async (interaction: CommandInteraction | ButtonInteraction, member?: GuildMember): Promise<unknown> => {
if (!interaction.guild) return; if (!interaction.guild) return;
const { log, NucleusColors, renderUser, entry, renderDelta } = client.logger; const { log, NucleusColors, renderUser, entry, renderDelta } = client.logger;
const member = interaction.options.getMember("user") as GuildMember; let time: { days: number; hours: number; minutes: number; seconds: number } | null = null;
const time: { days: number; hours: number; minutes: number; seconds: number } = { if (!interaction.isButton()) {
days: (interaction.options.get("days")?.value as number | null) ?? 0, member = interaction.options.getMember("user") as GuildMember;
hours: (interaction.options.get("hours")?.value as number | null) ?? 0, time = {
minutes: (interaction.options.get("minutes")?.value as number | null) ?? 0, days: (interaction.options.get("days")?.value as number | null) ?? 0,
seconds: (interaction.options.get("seconds")?.value as number | null) ?? 0 hours: (interaction.options.get("hours")?.value as number | null) ?? 0,
}; minutes: (interaction.options.get("minutes")?.value as number | null) ?? 0,
seconds: (interaction.options.get("seconds")?.value as number | null) ?? 0
};
} else {
time = {days: 0, hours: 0, minutes: 0, seconds: 0};
}
if (!member) return;
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)
@ -198,7 +204,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
"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!, member!.user, interaction.user, reason),
"An appeal ticket will be created when Confirm is clicked", "An appeal ticket will be created when Confirm is clicked",
null, null,
"CONTROL.TICKET", "CONTROL.TICKET",
@ -275,12 +281,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
new ButtonBuilder() new ButtonBuilder()
.setStyle(ButtonStyle.Link) .setStyle(ButtonStyle.Link)
.setLabel(config.moderation.mute.text) .setLabel(config.moderation.mute.text)
.setURL( .setURL(config.moderation.mute.link.replaceAll("{id}", member.id))
config.moderation.mute.link.replaceAll(
"{id}",
(interaction.options.getMember("user") as GuildMember).id
)
)
) )
); );
} }
@ -399,14 +400,19 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
}); });
}; };
const check = async (interaction: CommandInteraction, partial: boolean = false) => { const check = (interaction: CommandInteraction | ButtonInteraction, partial: boolean = false, target?: GuildMember) => {
if (!interaction.guild) return; if (!interaction.guild) return;
const member = interaction.member as GuildMember; const member = interaction.member as GuildMember;
// Check if the user has moderate_members permission // Check if the user has moderate_members permission
if (!member.permissions.has("ModerateMembers")) return "You do not have the *Moderate Members* permission"; if (!member.permissions.has("ModerateMembers")) return "You do not have the *Moderate Members* permission";
if (partial) return true; if (partial) return true;
const me = interaction.guild.members.me!; const me = interaction.guild.members.me!;
const apply = interaction.options.getMember("user") as GuildMember; let apply;
if (interaction.isButton()) {
apply = target!;
} else {
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;

@ -1,5 +1,5 @@
import { LinkWarningFooter } from "./../../utils/defaults.js"; import { LinkWarningFooter } from "./../../utils/defaults.js";
import { ActionRowBuilder, ButtonBuilder, CommandInteraction, GuildMember, ButtonStyle, Message } from "discord.js"; import { ActionRowBuilder, ButtonBuilder, CommandInteraction, GuildMember, ButtonStyle, Message, ButtonInteraction, ModalBuilder, TextInputBuilder, TextInputStyle } from "discord.js";
import type { SlashCommandSubcommandBuilder } from "discord.js"; import type { SlashCommandSubcommandBuilder } 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";
@ -17,31 +17,37 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
option.setName("name").setDescription("The name to set | Leave blank to clear").setRequired(false) option.setName("name").setDescription("The name to set | Leave blank to clear").setRequired(false)
); );
const callback = async (interaction: CommandInteraction): Promise<unknown> => { const callback = async (interaction: CommandInteraction | ButtonInteraction, member?: GuildMember): Promise<unknown> => {
const { log, NucleusColors, entry, renderDelta, renderUser } = client.logger; const { log, NucleusColors, entry, renderDelta, renderUser } = client.logger;
let newNickname;
if (!interaction.isButton()) {
member = interaction.options.getMember("user") as GuildMember;
newNickname = interaction.options.get("name")?.value as string | undefined;
}
if (!member) return;
// TODO:[Modals] Replace this with a modal // TODO:[Modals] Replace this with a modal
let notify = true; let notify = false;
let confirmation; let confirmation;
let timedOut = false; let timedOut = false;
let success = false; let success = false;
let createAppealTicket = false; let createAppealTicket = false;
let firstRun = true; let firstRun = !interaction.isButton();
do { do {
confirmation = await new confirmationMessage(interaction) confirmation = await new confirmationMessage(interaction)
.setEmoji("PUNISH.NICKNAME.RED") .setEmoji("PUNISH.NICKNAME.RED")
.setTitle("Nickname") .setTitle("Nickname")
.setDescription( .setDescription(
keyValueList({ keyValueList({
user: renderUser(interaction.options.getUser("user")!), user: renderUser(member.user),
"new nickname": `${ "new nickname": `${
(interaction.options.get("name")?.value as string) newNickname
? (interaction.options.get("name")?.value as string) ? newNickname
: "*No nickname*" : "*No nickname*"
}` }`
}) + }) +
`Are you sure you want to ${ `Are you sure you want to ${
(interaction.options.get("name")?.value as string) ? "change" : "clear" newNickname ? "change" : "clear"
} <@!${(interaction.options.getMember("user") as GuildMember).id}>'s nickname?` } <@!${member.id}>'s nickname?`
) )
.setColor("Danger") .setColor("Danger")
.addCustomBoolean( .addCustomBoolean(
@ -51,7 +57,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
async () => async () =>
await create( await create(
interaction.guild!, interaction.guild!,
interaction.options.getUser("user")!, member!.user,
interaction.user, interaction.user,
"Nickname changed" "Nickname changed"
), ),
@ -70,6 +76,25 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
"ICONS.NOTIFY." + (notify ? "ON" : "OFF"), "ICONS.NOTIFY." + (notify ? "ON" : "OFF"),
notify notify
) )
.addModal(
"Change nickname",
"ICONS.EDIT",
"modal",
newNickname ?? "",
new ModalBuilder()
.setTitle("Editing nickname")
.addComponents(
new ActionRowBuilder<TextInputBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("default")
.setLabel("Nickname")
.setMaxLength(32)
.setRequired(false)
.setStyle(TextInputStyle.Short)
.setValue(newNickname ? newNickname : " ")
)
)
)
.setFailedMessage("No changes were made", "Success", "PUNISH.NICKNAME.GREEN") .setFailedMessage("No changes were made", "Success", "PUNISH.NICKNAME.GREEN")
.send(!firstRun); .send(!firstRun);
firstRun = false; firstRun = false;
@ -79,6 +104,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
notify = confirmation.components["notify"]!.active; notify = confirmation.components["notify"]!.active;
createAppealTicket = confirmation.components["appeal"]!.active; createAppealTicket = confirmation.components["appeal"]!.active;
} }
if (confirmation.modals) newNickname = confirmation.modals![0]!.value
} while (!timedOut && !success); } while (!timedOut && !success);
if (timedOut || !success) return; if (timedOut || !success) return;
let dmSent = false; let dmSent = false;
@ -96,10 +122,10 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setTitle("Nickname changed") .setTitle("Nickname changed")
.setDescription( .setDescription(
`Your nickname was ${ `Your nickname was ${
(interaction.options.get("name")?.value as string) ? "changed" : "cleared" newNickname ? "changed" : "cleared"
} in ${interaction.guild!.name}.` + } in ${interaction.guild!.name}.` +
((interaction.options.get("name")?.value as string) (newNickname
? `\nIt is now: ${interaction.options.get("name")?.value as string}` ? `\nIt is now: ${newNickname}`
: "") + : "") +
"\n\n" + "\n\n" +
(createAppealTicket (createAppealTicket
@ -122,26 +148,22 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setURL( .setURL(
config.moderation.nick.link.replaceAll( config.moderation.nick.link.replaceAll(
"{id}", "{id}",
(interaction.options.getMember("user") as GuildMember).id member.id
) )
) )
) )
); );
} }
dmMessage = await (interaction.options.getMember("user") as GuildMember).send(messageData); dmMessage = await member.send(messageData);
dmSent = true; dmSent = true;
} }
} catch { } catch {
dmSent = false; dmSent = false;
} }
let member: GuildMember;
let before: string | null; let before: string | null;
let nickname: string | undefined;
try { try {
member = interaction.options.getMember("user") as GuildMember;
before = member.nickname; before = member.nickname;
nickname = interaction.options.get("name")?.value as string | undefined; member.setNickname(newNickname ?? null, "Nucleus Nickname command");
member.setNickname(nickname ?? null, "Nucleus Nickname command");
await client.database.history.create( await client.database.history.create(
"nickname", "nickname",
interaction.guild!.id, interaction.guild!.id,
@ -149,7 +171,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
interaction.user, interaction.user,
null, null,
before, before,
nickname newNickname
); );
} catch { } catch {
await interaction.editReply({ await interaction.editReply({
@ -175,9 +197,9 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
timestamp: Date.now() timestamp: Date.now()
}, },
list: { list: {
memberId: entry(member.id, `\`${member.id}\``), member: entry(member.id, renderUser(member.user)),
before: entry(before, before ?? "*No nickname set*"), before: entry(before, before ?? "*No nickname set*"),
after: entry(nickname ?? null, nickname ?? "*No nickname set*"), after: entry(newNickname ?? null, newNickname ?? "*No nickname set*"),
updated: entry(Date.now(), renderDelta(Date.now())), updated: entry(Date.now(), renderDelta(Date.now())),
updatedBy: entry(interaction.user.id, renderUser(interaction.user)) updatedBy: entry(interaction.user.id, renderUser(interaction.user))
}, },
@ -210,13 +232,18 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
}); });
}; };
const check = async (interaction: CommandInteraction, partial: boolean = false) => { const check = async (interaction: CommandInteraction | ButtonInteraction, partial: boolean, target?: GuildMember) => {
const member = interaction.member as GuildMember; const member = interaction.member as GuildMember;
// Check if the user has manage_nicknames permission // Check if the user has manage_nicknames permission
if (!member.permissions.has("ManageNicknames")) return "You do not have the *Manage Nicknames* permission"; if (!member.permissions.has("ManageNicknames")) return "You do not have the *Manage Nicknames* permission";
if (partial) return true; if (partial) return true;
const me = interaction.guild!.members.me!; const me = interaction.guild!.members.me!;
const apply = interaction.options.getMember("user") as GuildMember; let apply: GuildMember;
if (interaction.isButton()) {
apply = target!;
} else {
apply = interaction.options.getMember("user") as GuildMember;
}
const memberPos = member.roles.cache.size ? member.roles.highest.position : 0; const memberPos = member.roles.cache.size ? member.roles.highest.position : 0;
const mePos = me.roles.cache.size ? me.roles.highest.position : 0; const mePos = me.roles.cache.size ? me.roles.highest.position : 0;
const applyPos = apply.roles.cache.size ? apply.roles.highest.position : 0; const applyPos = apply.roles.cache.size ? apply.roles.highest.position : 0;

@ -1,4 +1,4 @@
import Discord, { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; import Discord, { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle, ButtonInteraction } from "discord.js";
import type { SlashCommandSubcommandBuilder } from "discord.js"; import type { SlashCommandSubcommandBuilder } 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";
@ -14,9 +14,11 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.setDescription("Warns a user") .setDescription("Warns a user")
.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 | ButtonInteraction, member?: GuildMember): Promise<unknown> => {
if (interaction.guild === null) return; if (!interaction.guild) return;
const { log, NucleusColors, renderUser, entry } = client.logger; const { log, NucleusColors, renderUser, entry } = client.logger;
if (!interaction.isButton()) member = interaction.options.getMember("user") as GuildMember;
if (!member) return;
// TODO:[Modals] Replace this with a modal // TODO:[Modals] Replace this with a modal
let reason: string | null = null; let reason: string | null = null;
let notify = true; let notify = true;
@ -30,9 +32,9 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setTitle("Warn") .setTitle("Warn")
.setDescription( .setDescription(
keyValueList({ keyValueList({
user: renderUser(interaction.options.getUser("user")!), user: renderUser(member.user),
reason: reason ? "\n> " + reason.replaceAll("\n", "\n> ") : "*No reason provided*" reason: reason ? "\n> " + reason.replaceAll("\n", "\n> ") : "*No reason provided*"
}) + `Are you sure you want to warn <@!${(interaction.options.getMember("user") as GuildMember).id}>?` }) + `Are you sure you want to warn <@!${member.id}>?`
) )
.setColor("Danger") .setColor("Danger")
.addCustomBoolean( .addCustomBoolean(
@ -40,7 +42,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
"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!, member!.user, interaction.user, reason),
"An appeal ticket will be created", "An appeal ticket will be created",
null, null,
"CONTROL.TICKET", "CONTROL.TICKET",
@ -111,13 +113,13 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setURL( .setURL(
config.moderation.warn.link.replaceAll( config.moderation.warn.link.replaceAll(
"{id}", "{id}",
(interaction.options.getMember("user") as GuildMember).id member.id
) )
) )
) )
); );
} }
await (interaction.options.getMember("user") as GuildMember).send(messageData); await member.send(messageData);
dmSent = true; dmSent = true;
} }
} catch (e) { } catch (e) {
@ -134,8 +136,8 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
}, },
list: { list: {
user: entry( user: entry(
(interaction.options.getMember("user") as GuildMember).user.id, member.user.id,
renderUser((interaction.options.getMember("user") as GuildMember).user) renderUser(member.user)
), ),
warnedBy: entry(interaction.member!.user.id, renderUser(interaction.member!.user as Discord.User)), warnedBy: entry(interaction.member!.user.id, renderUser(interaction.member!.user as Discord.User)),
reason: reason ? reason : "*No reason provided*" reason: reason ? reason : "*No reason provided*"
@ -152,7 +154,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
await client.database.history.create( await client.database.history.create(
"warn", "warn",
interaction.guild.id, interaction.guild.id,
(interaction.options.getMember("user") as GuildMember).user, member.user,
interaction.user, interaction.user,
reason reason
); );
@ -177,7 +179,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
components: [] components: []
}); });
} else { } else {
const canSeeChannel = (interaction.options.getMember("user") as GuildMember) const canSeeChannel = member
.permissionsIn(interaction.channel as Discord.TextChannel) .permissionsIn(interaction.channel as Discord.TextChannel)
.has("ViewChannel"); .has("ViewChannel");
const m = (await interaction.editReply({ const m = (await interaction.editReply({
@ -235,9 +237,9 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
.setDescription("You have been warned" + (reason ? ` for:\n> ${reason}` : ".")) .setDescription("You have been warned" + (reason ? ` for:\n> ${reason}` : "."))
.setStatus("Danger") .setStatus("Danger")
], ],
content: `<@!${(interaction.options.getMember("user") as GuildMember).id}>`, content: `<@!${member.id}>`,
allowedMentions: { allowedMentions: {
users: [(interaction.options.getMember("user") as GuildMember).id] users: [member.id]
} }
}); });
return await interaction.editReply({ return await interaction.editReply({
@ -271,7 +273,7 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
} else if (component.customId === "ticket") { } else if (component.customId === "ticket") {
const ticketChannel = await create( const ticketChannel = await create(
interaction.guild, interaction.guild,
interaction.options.getUser("user")!, member.user,
interaction.user, interaction.user,
reason, reason,
"Warn Notification" "Warn Notification"
@ -302,13 +304,17 @@ const callback = async (interaction: CommandInteraction): Promise<unknown> => {
} }
}; };
const check = (interaction: CommandInteraction, partial: boolean = false) => { const check = (interaction: CommandInteraction | ButtonInteraction, partial: boolean = false, target?: GuildMember) => {
if (!interaction.guild) return; if (!interaction.guild) return;
const member = interaction.member as GuildMember; const member = interaction.member as GuildMember;
if (!member.permissions.has("ModerateMembers")) return "You do not have the *Moderate Members* permission"; if (!member.permissions.has("ModerateMembers")) return "You do not have the *Moderate Members* permission";
if (partial) return true; if (partial) return true;
const apply = interaction.options.getMember("user") as GuildMember | null; let apply: GuildMember;
if (apply === null) return "That member is not in the server"; if (interaction.isButton()) {
apply = target!;
} else {
apply = interaction.options.getMember("user") as GuildMember;
}
const memberPos = member.roles.cache.size ? member.roles.highest.position : 0; const memberPos = member.roles.cache.size ? member.roles.highest.position : 0;
const applyPos = apply.roles.cache.size ? apply.roles.highest.position : 0; const applyPos = apply.roles.cache.size ? apply.roles.highest.position : 0;
// Do not allow warning bots // Do not allow warning bots

@ -28,7 +28,10 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
.setDescription(description) .setDescription(description)
.setStatus("Success") .setStatus("Success")
.setEmoji("SETTINGS.STATS.GREEN") .setEmoji("SETTINGS.STATS.GREEN")
], components: [new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder().setCustomId("admin").setLabel("Admin Panel").setStyle(ButtonStyle.Primary))] ], components: [new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder().setCustomId("admin").setLabel("Admin Panel").setStyle(ButtonStyle.Primary),
new ButtonBuilder().setCustomId("mod:nickname:599498449733550102").setLabel("Testing").setStyle(ButtonStyle.Primary)
)]
}); });
const modal = new ModalBuilder() const modal = new ModalBuilder()

@ -1069,17 +1069,19 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
closed = true; closed = true;
continue; continue;
} }
await i.deferUpdate();
if (i.isButton()) { if (i.isButton()) {
await i.deferUpdate();
await client.database.guilds.write(interaction.guild.id, { filters: config }); await client.database.guilds.write(interaction.guild.id, { filters: config });
await client.memory.forceUpdate(interaction.guild.id); await client.memory.forceUpdate(interaction.guild.id);
} else { } else {
switch (i.values[0]) { switch (i.values[0]) {
case "invites": { case "invites": {
i.deferUpdate();
config.invite = await inviteMenu(i, m, config.invite); config.invite = await inviteMenu(i, m, config.invite);
break; break;
} }
case "mentions": { case "mentions": {
i.deferUpdate();
config.pings = await mentionMenu(i, m, config.pings); config.pings = await mentionMenu(i, m, config.pings);
break; break;
} }
@ -1088,15 +1090,18 @@ const callback = async (interaction: CommandInteraction): Promise<void> => {
break; break;
} }
case "malware": { case "malware": {
i.deferUpdate();
config.malware = !config.malware; config.malware = !config.malware;
break; break;
} }
case "images": { case "images": {
i.deferUpdate();
const next = await imageMenu(i, m, config.images); const next = await imageMenu(i, m, config.images);
config.images = next; config.images = next;
break; break;
} }
case "clean": { case "clean": {
i.deferUpdate();
const next = await cleanMenu(i, m, config.clean); const next = await cleanMenu(i, m, config.clean);
config.clean = next; config.clean = next;
break; break;

@ -49,6 +49,14 @@
"RULES": "990213153080115250", "RULES": "990213153080115250",
"FORUM": "1061706437526552716", "FORUM": "1061706437526552716",
"CATEGORY": "1064943289708597348" "CATEGORY": "1064943289708597348"
},
"FLAGS": {
"RED": "1082719687219101800",
"YELLOW": "1082719684060794890",
"GREEN": "1082719681326108763",
"BLUE": "1082719679161843734",
"PURPLE": "1082719686292156628",
"GRAY": "1082719682492125337"
} }
}, },
"CONTROL": { "CONTROL": {

@ -111,7 +111,6 @@ export async function callback(client: NucleusClient, before: GuildMember, after
timestamp: Date.now() timestamp: Date.now()
}, },
list: { list: {
memberId: entry(after.id, `\`${after.id}\``),
name: entry(after.user.id, renderUser(after.user)), name: entry(after.user.id, renderUser(after.user)),
before: entry(before.nickname, before.nickname ? before.nickname : "*None*"), before: entry(before.nickname, before.nickname ? before.nickname : "*None*"),
after: entry(after.nickname, after.nickname ? after.nickname : "*None*"), after: entry(after.nickname, after.nickname ? after.nickname : "*None*"),

@ -5,7 +5,7 @@ import { callback as statsChannelUpdate } from "../reflex/statsChannelUpdate.js"
export const event = "guildUpdate"; export const event = "guildUpdate";
export async function callback(client: NucleusClient, before: Guild, after: Guild) { export async function callback(client: NucleusClient, before: Guild, after: Guild) {
await statsChannelUpdate(client, after.members.me!); await statsChannelUpdate(after.members.me!.user, after);
const { getAuditLog, isLogging, log, NucleusColors, entry, renderUser, renderDelta } = client.logger; const { getAuditLog, isLogging, log, NucleusColors, entry, renderUser, renderDelta } = client.logger;
if (!(await isLogging(after.id, "guildUpdate"))) return; if (!(await isLogging(after.id, "guildUpdate"))) return;
const auditLog = (await getAuditLog(after, AuditLogEvent.GuildUpdate)).filter( const auditLog = (await getAuditLog(after, AuditLogEvent.GuildUpdate)).filter(

@ -4,13 +4,30 @@ import create from "../actions/tickets/create.js";
import close from "../actions/tickets/delete.js"; import close from "../actions/tickets/delete.js";
import createTranscript from "../premium/createTranscript.js"; import createTranscript from "../premium/createTranscript.js";
import type { Interaction } from "discord.js"; import type { ButtonInteraction, Interaction } from "discord.js";
import type Discord from "discord.js"; import type Discord from "discord.js";
import type { NucleusClient } from "../utils/client.js"; import type { NucleusClient } from "../utils/client.js";
import EmojiEmbed from "../utils/generateEmojiEmbed.js"; import EmojiEmbed from "../utils/generateEmojiEmbed.js";
import { callback as banCallback, check as banCheck } from "../commands/mod/ban.js";
import { callback as kickCallback, check as kickCheck } from "../commands/mod/kick.js";
import { callback as muteCallback, check as muteCheck } from "../commands/mod/mute.js";
import { callback as nicknameCallback, check as nicknameCheck } from "../commands/mod/nick.js";
import { callback as warnCallback, check as warnCheck } from "../commands/mod/warn.js";
export const event = "interactionCreate"; export const event = "interactionCreate";
async function errorMessage(interaction: ButtonInteraction, message: string) {
await interaction.reply({
embeds: [new EmojiEmbed()
.setDescription(message)
.setStatus("Danger")
],
ephemeral: true,
components: []
});
}
async function interactionCreate(interaction: Interaction) { async function interactionCreate(interaction: Interaction) {
if (interaction.isButton()) { if (interaction.isButton()) {
switch (interaction.customId) { switch (interaction.customId) {
@ -36,6 +53,35 @@ async function interactionCreate(interaction: Interaction) {
return await modifySuggestion(interaction, false); return await modifySuggestion(interaction, false);
} }
} }
// Mod actions
if (interaction.customId.startsWith("mod:")) {
const action = interaction.customId.split(":")[1];
const memberId = interaction.customId.split(":")[2];
const member = await interaction.guild?.members.fetch(memberId!);
switch (action) {
case "kick": {
const check = await kickCheck(interaction, false, member)
if (check !== true) return await errorMessage(interaction, check!);
return await kickCallback(interaction, member);
} case "ban": {
const check = await banCheck(interaction, false, member)
if (check !== true) return await errorMessage(interaction, check!);
return await banCallback(interaction, member);
} case "mute": {
const check = await muteCheck(interaction, false, member)
if (check !== true) return await errorMessage(interaction, check!);
return await muteCallback(interaction, member);
} case "nickname": {
const check = await nicknameCheck(interaction, false, member)
if (check !== true) return await errorMessage(interaction, check || "Something went wrong");
return await nicknameCallback(interaction, member);
} case "warn": {
const check = await warnCheck(interaction, false, member)
if (check !== true) return await errorMessage(interaction, check!);
return await warnCallback(interaction, member);
}
}
}
} }
} }

@ -2,12 +2,14 @@ import type { GuildMember } from "discord.js";
import { callback as statsChannelAdd } from "../reflex/statsChannelUpdate.js"; import { callback as statsChannelAdd } from "../reflex/statsChannelUpdate.js";
import { callback as welcome } from "../reflex/welcome.js"; import { callback as welcome } from "../reflex/welcome.js";
import type { NucleusClient } from "../utils/client.js"; import type { NucleusClient } from "../utils/client.js";
import { doMemberChecks } from "../reflex/scanners.js";
export const event = "guildMemberAdd"; export const event = "guildMemberAdd";
export async function callback(client: NucleusClient, member: GuildMember) { export async function callback(client: NucleusClient, member: GuildMember) {
welcome(client, member); welcome(member);
statsChannelAdd(client, member); statsChannelAdd(member.user, member.guild);
doMemberChecks(member, member.guild)
const { log, isLogging, NucleusColors, entry, renderUser, renderDelta } = client.logger; const { log, isLogging, NucleusColors, entry, renderUser, renderDelta } = client.logger;
if (!(await isLogging(member.guild.id, "guildMemberUpdate"))) return; if (!(await isLogging(member.guild.id, "guildMemberUpdate"))) return;
await client.database.history.create("join", member.guild.id, member.user, null, null); await client.database.history.create("join", member.guild.id, member.user, null, null);

@ -30,7 +30,7 @@ export async function callback(_client: NucleusClient, message: Message) {
if (message.author.bot) return; if (message.author.bot) return;
if (message.channel.isDMBased()) return; if (message.channel.isDMBased()) return;
try { try {
await statsChannelUpdate(client, await message.guild.members.fetch(message.author.id)); await statsChannelUpdate((await message.guild.members.fetch(message.author.id)).user, message.guild);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }

@ -8,6 +8,9 @@ import { createHash } from "crypto";
import * as nsfwjs from 'nsfwjs'; import * as nsfwjs from 'nsfwjs';
import * as clamscan from 'clamscan' import * as clamscan from 'clamscan'
import * as tf from "@tensorflow/tfjs-node"; import * as tf from "@tensorflow/tfjs-node";
import EmojiEmbed from "../utils/generateEmojiEmbed.js";
import getEmojiByName from "../utils/getEmojiByName.js";
import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
interface NSFWSchema { interface NSFWSchema {
nsfw: boolean; nsfw: boolean;
@ -164,7 +167,8 @@ export async function MalwareCheck(element: string): Promise<boolean> {
} }
} }
export function TestString(string: string, soft: string[], strict: string[]): object | null { export function TestString(string: string, soft: string[], strict: string[], enabled?: boolean): {word: string, type: string} | null {
if (!enabled) return null;
for (const word of strict) { for (const word of strict) {
if (string.toLowerCase().includes(word)) { if (string.toLowerCase().includes(word)) {
return { word: word, type: "strict" }; return { word: word, type: "strict" };
@ -173,7 +177,7 @@ export function TestString(string: string, soft: string[], strict: string[]): ob
for (const word of soft) { for (const word of soft) {
for (const word2 of string.match(/[a-z]+/gi) ?? []) { for (const word2 of string.match(/[a-z]+/gi) ?? []) {
if (word2 === word) { if (word2 === word) {
return { word: word, type: "strict" }; return { word: word, type: "soft" };
} }
} }
} }
@ -188,3 +192,76 @@ export async function TestImage(url: string): Promise<string | null> {
}); });
return text; return text;
} }
export async function doMemberChecks(member: Discord.GuildMember, guild: Discord.Guild): Promise<void> {
if (member.user.bot) return;
const guildData = await client.database.guilds.read(guild.id);
if (!guildData.logging.staff.channel) return;
const [ loose, strict ] = [guildData.filters.wordFilter.words.loose, guildData.filters.wordFilter.words.strict];
// Does the username contain filtered words
const usernameCheck = TestString(member.user.username, loose, strict, guildData.filters.wordFilter.enabled);
// Does the nickname contain filtered words
const nicknameCheck = TestString(member.nickname ?? "", loose, strict, guildData.filters.wordFilter.enabled);
// Does the profile picture contain filtered words
const avatarTextCheck = TestString(await TestImage(member.user.displayAvatarURL({ forceStatic: true })) ?? "", loose, strict, guildData.filters.wordFilter.enabled);
// Is the profile picture NSFW
const avatarCheck = guildData.filters.images.NSFW && await NSFWCheck(member.user.displayAvatarURL({ forceStatic: true }));
// Does the username contain an invite
const inviteCheck = guildData.filters.invite.enabled && member.user.username.match(/discord\.gg\/[a-zA-Z0-9]+/gi) !== null;
// Does the nickname contain an invite
const nicknameInviteCheck = guildData.filters.invite.enabled && member.nickname?.match(/discord\.gg\/[a-zA-Z0-9]+/gi) !== null;
if (usernameCheck !== null || nicknameCheck !== null || avatarCheck || inviteCheck || nicknameInviteCheck || avatarTextCheck !== null) {
const infractions = [];
if (usernameCheck !== null) {
infractions.push(`Username contains a ${usernameCheck.type}ly filtered word (${usernameCheck.word})`);
} if (nicknameCheck !== null) {
infractions.push(`Nickname contains a ${nicknameCheck.type}ly filtered word (${nicknameCheck.word})`);
} if (avatarCheck) {
infractions.push("Profile picture is NSFW");
} if (inviteCheck) {
infractions.push("Username contains an invite");
} if (nicknameInviteCheck) {
infractions.push("Nickname contains an invite");
} if (avatarTextCheck !== null) {
infractions.push(`Profile picture contains a ${avatarTextCheck.type}ly filtered word: ${avatarTextCheck.word}`);
}
if (infractions.length === 0) return;
// This is bad - Warn in the staff notifications channel
const filter = getEmojiByName("ICONS.FILTER");
const channel = guild.channels.cache.get(guildData.logging.staff.channel) as Discord.TextChannel;
const embed = new EmojiEmbed()
.setTitle("Member Flagged")
.setEmoji("ICONS.FLAGS.RED")
.setStatus("Danger")
.setDescription(`**Member:** ${member.user.username} (<@${member.user.id}>)\n\n` +
infractions.map((element) => `${filter} ${element}`).join("\n")
)
await channel.send({
embeds: [embed],
components: [new ActionRowBuilder<ButtonBuilder>().addComponents(...[
new ButtonBuilder()
.setCustomId(`mod:warn:${member.user.id}`)
.setLabel("Warn")
.setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId(`mod:mute:${member.user.id}`)
.setLabel("Mute")
.setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId(`mod:kick:${member.user.id}`)
.setLabel("Kick")
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setCustomId(`mod:ban:${member.user.id}`)
.setLabel("Ban")
.setStyle(ButtonStyle.Danger),
].concat((usernameCheck !== null || nicknameCheck !== null || avatarTextCheck !== null) ? [
new ButtonBuilder()
.setCustomId(`mod:nickname:${member.user.id}`)
.setLabel("Change Name")
.setStyle(ButtonStyle.Primary)
] : []))]
});
}
}

@ -1,7 +1,6 @@
import { getCommandMentionByName } from "../utils/getCommandDataByName.js"; import { getCommandMentionByName } from "../utils/getCommandDataByName.js";
import type { Guild, User } from "discord.js"; import type { Guild, User } from "discord.js";
import type { NucleusClient } from "../utils/client.js"; import client from "../utils/client.js";
import type { GuildMember } from "discord.js";
import convertCurlyBracketString from "../utils/convertCurlyBracketString.js"; import convertCurlyBracketString from "../utils/convertCurlyBracketString.js";
import singleNotify from "../utils/singleNotify.js"; import singleNotify from "../utils/singleNotify.js";
@ -10,10 +9,8 @@ interface PropSchema {
name: string; name: string;
} }
export async function callback(client: NucleusClient, member?: GuildMember, guild?: Guild, user?: User) { export async function callback(user: User, guild: Guild) {
if (!member && !guild) return; guild = await client.guilds.fetch(guild.id);
guild = await client.guilds.fetch(member ? member.guild.id : guild!.id);
user = user ?? member!.user;
const config = await client.database.guilds.read(guild.id); const config = await client.database.guilds.read(guild.id);
Object.entries(config.stats).forEach(async ([channel, props]) => { Object.entries(config.stats).forEach(async ([channel, props]) => {
if ((props as PropSchema).enabled) { if ((props as PropSchema).enabled) {
@ -22,16 +19,16 @@ export async function callback(client: NucleusClient, member?: GuildMember, guil
string = await convertCurlyBracketString(string, user!.id, user!.username, guild!.name, guild!.members); string = await convertCurlyBracketString(string, user!.id, user!.username, guild!.name, guild!.members);
let fetchedChannel; let fetchedChannel;
try { try {
fetchedChannel = await guild!.channels.fetch(channel); fetchedChannel = await guild.channels.fetch(channel);
} catch (e) { } catch (e) {
fetchedChannel = null; fetchedChannel = null;
} }
if (!fetchedChannel) { if (!fetchedChannel) {
const deleted = config.stats[channel]; const deleted = config.stats[channel];
await client.database.guilds.write(guild!.id, null, `stats.${channel}`); await client.database.guilds.write(guild.id, null, `stats.${channel}`);
return singleNotify( return singleNotify(
"statsChannelDeleted", "statsChannelDeleted",
guild!.id, guild.id,
`One or more of your stats channels have been deleted. You can use ${getCommandMentionByName( `One or more of your stats channels have been deleted. You can use ${getCommandMentionByName(
"settings/stats" "settings/stats"
)}.\n` + `The channels name was: ${deleted!.name}`, )}.\n` + `The channels name was: ${deleted!.name}`,

@ -1,12 +1,11 @@
import { getCommandMentionByName } from "./../utils/getCommandDataByName.js"; import { getCommandMentionByName } from "./../utils/getCommandDataByName.js";
import type { NucleusClient } from "../utils/client.js";
import convertCurlyBracketString from "../utils/convertCurlyBracketString.js"; import convertCurlyBracketString from "../utils/convertCurlyBracketString.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";
import { GuildChannel, GuildMember, BaseGuildTextChannel } from "discord.js"; import { GuildChannel, GuildMember, BaseGuildTextChannel } from "discord.js";
import singleNotify from "../utils/singleNotify.js"; import singleNotify from "../utils/singleNotify.js";
export async function callback(_client: NucleusClient, member: GuildMember) { export async function callback(member: GuildMember) {
if (member.user.bot) return; if (member.user.bot) return;
const config = await client.database.guilds.read(member.guild.id); const config = await client.database.guilds.read(member.guild.id);
if (!config.welcome.enabled) return; if (!config.welcome.enabled) return;

@ -1,4 +1,4 @@
import { TextInputBuilder } from "discord.js"; import { ButtonInteraction, TextInputBuilder } from "discord.js";
import Discord, { import Discord, {
CommandInteraction, CommandInteraction,
Message, Message,
@ -24,7 +24,7 @@ interface CustomBoolean<T> {
} }
class confirmationMessage { class confirmationMessage {
interaction: CommandInteraction; interaction: CommandInteraction | ButtonInteraction;
title = ""; title = "";
emoji = ""; emoji = "";
redEmoji: string | null = null; redEmoji: string | null = null;
@ -37,7 +37,9 @@ class confirmationMessage {
inverted = false; inverted = false;
reason: string | null = null; reason: string | null = null;
constructor(interaction: CommandInteraction) { modals: {buttonText: string, emoji: string, customId: string, modal: Discord.ModalBuilder, value: string | undefined}[] = [];
constructor(interaction: CommandInteraction | ButtonInteraction) {
this.interaction = interaction; this.interaction = interaction;
} }
@ -98,11 +100,17 @@ class confirmationMessage {
this.reason = reason; this.reason = reason;
return this; return this;
} }
addModal(buttonText: string, emoji: string, customId: string, current: string, modal: Discord.ModalBuilder) {
modal.setCustomId(customId);
this.modals.push({buttonText, emoji, customId, modal, value: current});
return this;
}
async send(editOnly?: boolean): Promise<{ async send(editOnly?: boolean): Promise<{
success?: boolean; success?: boolean;
cancelled?: boolean; cancelled?: boolean;
components?: Record<string, CustomBoolean<unknown>>; components?: Record<string, CustomBoolean<unknown>>;
newReason?: string; newReason?: string;
modals?: {buttonText: string, emoji: string, customId: string, modal: Discord.ModalBuilder, value: string | undefined}[];
}> { }> {
let cancelled = false; let cancelled = false;
let success: boolean | undefined = undefined; let success: boolean | undefined = undefined;
@ -131,6 +139,16 @@ class confirmationMessage {
if (v.emoji !== undefined) button.setEmoji(getEmojiByName(v.emoji, "id")); if (v.emoji !== undefined) button.setEmoji(getEmojiByName(v.emoji, "id"));
fullComponents.push(button); fullComponents.push(button);
}); });
for (const modal of this.modals) {
fullComponents.push(
new Discord.ButtonBuilder()
.setCustomId(modal.customId)
.setLabel(modal.buttonText)
.setStyle(ButtonStyle.Primary)
.setEmoji(getEmojiByName(modal.emoji, "id"))
.setDisabled(false)
);
}
if (this.reason !== null) if (this.reason !== null)
fullComponents.push( fullComponents.push(
new Discord.ButtonBuilder() new Discord.ButtonBuilder()
@ -183,7 +201,6 @@ class confirmationMessage {
m = (await this.interaction.reply(object)) as unknown as Message; m = (await this.interaction.reply(object)) as unknown as Message;
} }
} catch (e) { } catch (e) {
console.log(e);
cancelled = true; cancelled = true;
continue; continue;
} }
@ -195,7 +212,7 @@ class confirmationMessage {
time: 300000 time: 300000
}); });
} catch (e) { } catch (e) {
success = false; success = false
break; break;
} }
if (component.customId === "yes") { if (component.customId === "yes") {
@ -273,6 +290,46 @@ class confirmationMessage {
returnComponents = true; returnComponents = true;
continue; continue;
} }
} else if (this.modals.map((m) => m.customId).includes(component.customId)) {
const chosenModal = this.modals.find((component => m => m.customId === component.customId)(component));
await component.showModal(chosenModal!.modal);
await this.interaction.editReply({
embeds: [
new EmojiEmbed()
.setTitle(this.title)
.setDescription("Modal opened. If you can't see it, click back and try again.")
.setStatus(this.color)
.setEmoji(this.emoji)
],
components: [
new ActionRowBuilder<Discord.ButtonBuilder>().addComponents(
new ButtonBuilder()
.setLabel("Back")
.setEmoji(getEmojiByName("CONTROL.LEFT", "id"))
.setStyle(ButtonStyle.Primary)
.setCustomId("back")
)
]
});
let out;
try {
out = (await modalInteractionCollector(
m,
this.interaction.user
)) as Discord.ModalSubmitInteraction | null;
} catch (e) {
console.log(e);
cancelled = true;
continue;
}
if (out === null || out.isButton()) {
continue;
}
if (out instanceof ModalSubmitInteraction) {
chosenModal!.value = out.fields.getTextInputValue("default");
}
returnComponents = true;
continue;
} else { } else {
component.deferUpdate(); component.deferUpdate();
this.customButtons[component.customId]!.active = !this.customButtons[component.customId]!.active; this.customButtons[component.customId]!.active = !this.customButtons[component.customId]!.active;
@ -297,17 +354,19 @@ class confirmationMessage {
], ],
components: [] components: []
}); });
return { success: false }; return { success: false, cancelled: returnValue.cancelled ?? false };
} }
if (returnComponents || success !== undefined) returnValue.components = this.customButtons; if (returnComponents || success !== undefined) returnValue.components = this.customButtons;
if (success !== undefined) returnValue.success = success; if (success !== undefined) returnValue.success = success;
if (newReason) returnValue.newReason = newReason; if (newReason) returnValue.newReason = newReason;
returnValue.modals = this.modals;
const modals = this.modals;
const typedReturnValue = returnValue as const typedReturnValue = returnValue as
| { cancelled: true } | { cancelled: true }
| { success: boolean; components: Record<string, CustomBoolean<unknown>>; newReason?: string } | { success: boolean; components: Record<string, CustomBoolean<unknown>>; modals: typeof modals; newReason?: string }
| { newReason: string; components: Record<string, CustomBoolean<unknown>> } | { newReason: string; components: Record<string, CustomBoolean<unknown>>; modals: typeof modals }
| { components: Record<string, CustomBoolean<unknown>> }; | { components: Record<string, CustomBoolean<unknown>>; modals: typeof modals };
return typedReturnValue; return typedReturnValue;
} }

@ -14,14 +14,17 @@ import * as crypto from "crypto";
import _ from "lodash"; import _ from "lodash";
import defaultData from "../config/default.js"; import defaultData from "../config/default.js";
const username = encodeURIComponent(config.mongoOptions.username); let username, password;
const password = encodeURIComponent(config.mongoOptions.password);
// @ts-expect-error
if (Object.keys(config.mongoOptions).includes("username")) username = encodeURIComponent(config.mongoOptions.username);
// @ts-expect-error
if (Object.keys(config.mongoOptions).includes("password")) password = encodeURIComponent(config.mongoOptions.password);
const mongoClient = new MongoClient( const mongoClient = new MongoClient(
username username
? `mongodb://${username}:${password}@${config.mongoOptions.host}?authMechanism=DEFAULT` ? `mongodb://${username}:${password}@${config.mongoOptions.host}?authMechanism=DEFAULT&authSource=${config.mongoOptions.authSource}`
: `mongodb://${config.mongoOptions.host}`, : `mongodb://${config.mongoOptions.host}`
{ authSource: config.mongoOptions.authSource }
); );
await mongoClient.connect(); await mongoClient.connect();
const database = mongoClient.db(); const database = mongoClient.db();
@ -221,7 +224,7 @@ interface TranscriptSchema {
interface findDocSchema { interface findDocSchema {
channelID: string; channelID: string;
messageID: string; messageID: string;
code: string; transcript: string;
} }
export class Transcript { export class Transcript {
@ -284,20 +287,16 @@ export class Transcript {
async deleteAll(guild: string) { async deleteAll(guild: string) {
// console.log("Transcript delete") // console.log("Transcript delete")
const filteredDocs = await this.transcripts.find({ guild: guild }).toArray(); const filteredDocs = await this.transcripts.find({ guild: guild }).toArray();
const filteredDocs1 = await this.messageToTranscript.find({ guild: guild }).toArray();
for (const doc of filteredDocs) { for (const doc of filteredDocs) {
await this.transcripts.deleteOne({ code: doc.code }); await this.transcripts.deleteOne({ code: doc.code });
} }
for (const doc of filteredDocs1) {
await this.messageToTranscript.deleteOne({ code: doc.code });
}
} }
async readEncrypted(code: string) { async readEncrypted(code: string) {
// console.log("Transcript read") // console.log("Transcript read")
let doc: TranscriptSchema | null = await this.transcripts.findOne({ code: code }); let doc: TranscriptSchema | null = await this.transcripts.findOne({ code: code });
let findDoc: findDocSchema | null = null; let findDoc: findDocSchema | null = null;
if (!doc) findDoc = await this.messageToTranscript.findOne({ code: code }); if (!doc) findDoc = await this.messageToTranscript.findOne({ transcript: code });
if (findDoc) { if (findDoc) {
const message = await ( const message = await (
client.channels.cache.get(findDoc.channelID) as Discord.TextBasedChannel | null client.channels.cache.get(findDoc.channelID) as Discord.TextBasedChannel | null
@ -334,7 +333,7 @@ export class Transcript {
let doc: TranscriptSchema | null = await this.transcripts.findOne({ code: code }); let doc: TranscriptSchema | null = await this.transcripts.findOne({ code: code });
let findDoc: findDocSchema | null = null; let findDoc: findDocSchema | null = null;
console.log(doc); console.log(doc);
if (!doc) findDoc = await this.messageToTranscript.findOne({ code: code }); if (!doc) findDoc = await this.messageToTranscript.findOne({ transcript: code });
if (findDoc) { if (findDoc) {
const message = await ( const message = await (
client.channels.cache.get(findDoc.channelID) as Discord.TextBasedChannel | null client.channels.cache.get(findDoc.channelID) as Discord.TextBasedChannel | null
@ -837,6 +836,10 @@ export class Premium {
} }
} }
export class Plugins {
}
export interface GuildConfig { export interface GuildConfig {
id: string; id: string;
version: number; version: number;

Loading…
Cancel
Save