stats channels

pull/6/head
pineafan 3 years ago
parent 813bdf45f3
commit 02ba023698
No known key found for this signature in database
GPG Key ID: 0AEF25BAA0FB1C74

@ -1,4 +1,15 @@
{
"logging": {
"logs": {
"enabled": true,
"toLog": "3fffff",
"ignore": {
"users": [],
"roles": [],
"channels": []
}
}
},
"filters": {
"images": {
"NSFW": false,
@ -6,9 +17,12 @@
},
"malware": false,
"wordFilter": {
"enabled": false,
"enabled": true,
"words": {
"strict": [],
"strict": [
"meat",
"noon"
],
"loose": []
},
"allowed": {
@ -37,23 +51,10 @@
}
}
},
"welcome": {
"enabled": false,
"welcomeRole": null,
"channel": null,
"message": null
},
"stats": [
{
"enabled": true,
"channel": "9849175359",
"text": "{count} members | {count:bots} bots | {count:humans} humans"
}
],
"moderation": {
"mute": {
"timeout": true,
"role": null,
"role": "934941408849186856",
"text": null,
"link": null
},
@ -70,25 +71,24 @@
"link": null
},
"warn": {
"text": null,
"link": null
"text": "Test",
"link": "https://google.com"
},
"role": {
"role": null
}
},
"tracks": [
],
"logging": {
"logs": {
"enabled": true,
"toLog": "3fffff"
}
},
"roleMenu": {
"enabled": true,
"allowWebUI": true,
"options": [
]
"roleMenu": [],
"stats": [],
"tracks": [],
"welcome": {
"enabled": false,
"verificationRequired": {
"message": null,
"role": null
},
"welcomeRole": null,
"channel": null,
"message": null
}
}

@ -6,6 +6,7 @@
"body-parser": "^1.20.0",
"discord.js": "13.8.1",
"express": "^4.18.1",
"fuse.js": "^6.6.2",
"humanize": "^0.0.9",
"humanize-duration": "^3.27.1",
"jshaiku": "file:../haiku",

@ -5,7 +5,6 @@ import client from "../utils/client.js";
export async function create(guild: Discord.Guild, member: Discord.User, createdBy: Discord.User, reason: string) {
let config = await client.database.guilds.read(guild.id);
// @ts-ignore
const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger
let overwrites = [{
id: member,

@ -134,29 +134,27 @@ async function purgeByUser(member, guild) {
}
});
if (deleted) {
try {
const { log, NucleusColors, entry, renderUser, renderDelta } = member.client.logger
let data = {
meta:{
type: 'ticketPurge',
displayName: 'Tickets Purged',
calculateType: "ticketUpdate",
color: NucleusColors.red,
emoji: 'GUILD.TICKET.DELETE',
timestamp: new Date().getTime()
},
list: {
ticketFor: entry(member, renderUser(member)),
deletedBy: entry(null, "Member left server"),
deleted: entry(new Date().getTime(), renderDelta(new Date().getTime())),
ticketsDeleted: deleted,
},
hidden: {
guild: guild.id
}
const { log, NucleusColors, entry, renderUser, renderDelta } = member.client.logger
let data = {
meta:{
type: 'ticketPurge',
displayName: 'Tickets Purged',
calculateType: "ticketUpdate",
color: NucleusColors.red,
emoji: 'GUILD.TICKET.DELETE',
timestamp: new Date().getTime()
},
list: {
ticketFor: entry(member, renderUser(member)),
deletedBy: entry(null, "Member left server"),
deleted: entry(new Date().getTime(), renderDelta(new Date().getTime())),
ticketsDeleted: deleted,
},
hidden: {
guild: guild.id
}
log(data);
} catch {}
}
log(data);
}
}

@ -89,7 +89,6 @@ const runServer = (client: HaikuClient) => {
const secret = req.body.secret;
const data = req.body.data;
if (secret === client.config.verifySecret) {
console.table(data)
let guild = await client.guilds.fetch(client.roleMenu[code].guild);
if (!guild) { return res.status(404) }
let member = await guild.members.fetch(client.roleMenu[code].user);

@ -6,7 +6,7 @@ import client from "../utils/client.js"
import addPlural from "../utils/plurals.js";
import getEmojiByName from "../utils/getEmojiByName.js";
const command = new SlashCommandBuilder()
const command = new SlashCommandBuilder() // TODO: remove for release
.setName("categorise")
.setDescription("Categorises your servers channels")
@ -44,10 +44,8 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
for (let c of channels) {
// convert channel to a channel if its a string
let channel: any
console.log(c)
if (typeof c === "string") channel = interaction.guild.channels.cache.get(channel).id
// @ts-ignore
else channel = c[0].id
else channel = (c[0] as unknown as GuildChannel).id
console.log(channel)
if (!predicted[channel]) predicted[channel] = []
m = await interaction.editReply({embeds: [new EmojiEmbed()

@ -12,16 +12,14 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.setName("ban")
.setDescription("Bans a user from the server")
.addUserOption(option => option.setName("user").setDescription("The user to ban").setRequired(true))
.addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are banned | Default: Yes").setRequired(false)
.addChoices([["Yes", "yes"], ["No", "no"]])
)
.addIntegerOption(option => option.setName("delete").setDescription("The days of messages to delete | Default: 0").setMinValue(0).setMaxValue(7).setRequired(false))
.addNumberOption(option => option.setName("delete").setDescription("The days of messages to delete | Default: 0").setMinValue(0).setMaxValue(7).setRequired(false))
const callback = async (interaction: CommandInteraction): Promise<any> => {
const { renderUser } = client.logger
// TODO:[Modals] Replace this with a modal
let reason = null
let confirmation
let notify = true;
let confirmation;
while (true) {
confirmation = await new confirmationMessage(interaction)
.setEmoji("PUNISH.BAN.RED")
@ -30,22 +28,24 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
"user": renderUser(interaction.options.getUser("user")),
"reason": reason ? ("\n> " + ((reason ?? "").replaceAll("\n", "\n> "))) : "*No reason provided*"
})
+ `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n`
+ `The user **will${notify ? '' : ' not'}** be notified\n`
+ `${addPlurals(interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0, "day")} of messages will be deleted\n\n`
+ `Are you sure you want to ban <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
.setColor("Danger")
.addReasonButton(reason ?? "")
.send(reason !== null)
reason = reason ?? ""
if (confirmation.newReason === undefined) break
reason = confirmation.newReason
if (confirmation.cancelled) return
if (confirmation.success) break
if (confirmation.newReason) reason = confirmation.newReason
if (confirmation.components) notify = confirmation.components.notify.active
}
if (confirmation.success) {
let dmd = false
let dm;
let config = await client.database.guilds.read(interaction.guild.id);
try {
if (interaction.options.getString("notify") != "no") {
if (notify) {
dm = await (interaction.options.getMember("user") as GuildMember).send({
embeds: [new EmojiEmbed()
.setEmoji("PUNISH.BAN.RED")
@ -66,7 +66,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
try {
let member = (interaction.options.getMember("user") as GuildMember)
member.ban({
days: Number(interaction.options.getInteger("delete") ?? 0),
days: Number(interaction.options.getNumber("delete") ?? 0),
reason: reason ?? "No reason provided"
})
try { await client.database.history.create("ban", interaction.guild.id, member.user, interaction.user, reason) } catch {}
@ -104,7 +104,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
if (dmd) await dm.delete()
return
}
let failed = (dmd == false && interaction.options.getString("notify") != "no")
let failed = (dmd == false && notify)
await interaction.editReply({embeds: [new EmojiEmbed()
.setEmoji(`PUNISH.BAN.${failed ? "YELLOW" : "GREEN"}`)
.setTitle(`Ban`)

@ -14,7 +14,6 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.setDescription("Shows moderator information about a user")
.addUserOption(option => option.setName("user").setDescription("The user to get information about").setRequired(true))
const types = {
"warn": {emoji: "PUNISH.WARN.YELLOW", text: "Warned"},
"mute": {emoji: "PUNISH.MUTE.YELLOW", text: "Muted"},
@ -237,8 +236,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
} catch (e) { return }
if (i.customId === "modify") {
await i.showModal(new Discord.Modal().setCustomId("modal").setTitle(`Editing moderator note`).addComponents(
// @ts-ignore
new MessageActionRow().addComponents(new TextInputComponent()
new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
.setCustomId("note")
.setLabel("Note")
.setMaxLength(4000)

@ -12,14 +12,12 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.setName("kick")
.setDescription("Kicks a user from the server")
.addUserOption(option => option.setName("user").setDescription("The user to kick").setRequired(true))
.addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are kicked | Default: Yes").setRequired(false)
.addChoices([["Yes", "yes"], ["No", "no"]])
)
const callback = async (interaction: CommandInteraction): Promise<any> => {
const { renderUser } = client.logger
// TODO:[Modals] Replace this with a modal
let reason = null;
let notify = true;
let confirmation
while (true) {
confirmation = await new confirmationMessage(interaction)
@ -29,21 +27,25 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
"user": renderUser(interaction.options.getUser("user")),
"reason": reason ? ("\n> " + ((reason ?? "").replaceAll("\n", "\n> "))) : "*No reason provided*"
})
+ `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
+ `The user **will${notify ? '' : ' not'}** be notified\n\n`
+ `Are you sure you want to kick <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
.setColor("Danger")
.addReasonButton(reason ?? "")
.send(reason !== null)
reason = reason ?? ""
if (confirmation.newReason === undefined) break
reason = confirmation.newReason
if (confirmation.cancelled) return
if (confirmation.success) break
if (confirmation.newReason) reason = confirmation.newReason
if (confirmation.components) {
notify = confirmation.components.notify.active
}
}
if (confirmation.success) {
let dmd = false
let dm;
let config = await client.database.guilds.read(interaction.guild.id);
try {
if (interaction.options.getString("notify") != "no") {
if (notify) {
dm = await (interaction.options.getMember("user") as GuildMember).send({
embeds: [new EmojiEmbed()
.setEmoji("PUNISH.KICK.RED")
@ -65,8 +67,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
(interaction.options.getMember("user") as GuildMember).kick(reason ?? "No reason provided.")
let member = (interaction.options.getMember("user") as GuildMember)
try { await client.database.history.create("kick", interaction.guild.id, member.user, interaction.user, reason) } catch {}
// @ts-ignore
const { log, NucleusColors, entry, renderUser, renderDelta } = member.client.logger
const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger
let data = {
meta: {
type: 'memberKick',
@ -102,7 +103,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
if (dmd) await dm.delete()
return
}
let failed = (dmd == false && interaction.options.getString("notify") != "no")
let failed = (dmd == false && notify)
await interaction.editReply({embeds: [new EmojiEmbed()
.setEmoji(`PUNISH.KICK.${failed ? "YELLOW" : "GREEN"}`)
.setTitle(`Kick`)

@ -18,8 +18,6 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.addIntegerOption(option => option.setName("hours").setDescription("The number of hours to mute the user for | Default: 0").setMinValue(0).setMaxValue(23).setRequired(false))
.addIntegerOption(option => option.setName("minutes").setDescription("The number of minutes to mute the user for | Default: 0").setMinValue(0).setMaxValue(59).setRequired(false))
.addIntegerOption(option => option.setName("seconds").setDescription("The number of seconds to mute the user for | Default: 0").setMinValue(0).setMaxValue(59).setRequired(false))
.addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are muted | Default: yes").setRequired(false)
.addChoices([["Yes", "yes"], ["No", "no"]]))
const callback = async (interaction: CommandInteraction): Promise<any> => {
const { log, NucleusColors, renderUser, entry, renderDelta } = client.logger
@ -119,6 +117,8 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
}
// TODO:[Modals] Replace this with a modal
let reason = null;
let notify = true;
let createAppealTicket = false;
let confirmation;
while (true) {
confirmation = await new confirmationMessage(interaction)
@ -130,32 +130,39 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
"reason": reason ? ("\n> " + ((reason ?? "").replaceAll("\n", "\n> "))) : "*No reason provided*"
})
+ `The user will be ` + serverSettingsDescription + "\n"
+ `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
+ `The user **will${notify ? '' : ' not'}** be notified\n\n`
+ `Are you sure you want to mute <@!${user.id}>?`)
.setColor("Danger")
.addCustomBoolean(
"Create appeal ticket", !(await areTicketsEnabled(interaction.guild.id)),
async () => await create(interaction.guild, user.user, interaction.user, reason),
"An appeal ticket will be created when Confirm is clicked")
"appeal", "Create appeal ticket", !(await areTicketsEnabled(interaction.guild.id)),
async () => await create(interaction.guild, interaction.options.getUser("user"), interaction.user, reason),
"An appeal ticket will be created when Confirm is clicked", "CONTROL.TICKET", createAppealTicket)
.addCustomBoolean("notify", "Notify user", false, null, null, "ICONS.NOTIFY." + (notify ? "ON" : "OFF" ), notify)
.addReasonButton(reason ?? "")
.send(true)
reason = reason ?? ""
if (confirmation.newReason === undefined) break
reason = confirmation.newReason
if (confirmation.cancelled) return
if (confirmation.success) break
if (confirmation.newReason) reason = confirmation.newReason
if (confirmation.components) {
notify = confirmation.components.notify.active
createAppealTicket = confirmation.components.appeal.active
}
}
if (confirmation.success) {
let dmd = false
let dm;
let config = await client.database.guilds.read(interaction.guild.id);
try {
if (interaction.options.getString("notify") != "no") {
if (notify) {
dm = await user.send({
embeds: [new EmojiEmbed()
.setEmoji("PUNISH.MUTE.RED")
.setTitle("Muted")
.setDescription(`You have been muted in ${interaction.guild.name}` +
(reason ? ` for:\n> ${reason}` : ".\n\n" +
`You will be unmuted at: <t:${Math.round((new Date).getTime() / 1000) + muteTime}:D> at <t:${Math.round((new Date).getTime() / 1000) + muteTime}:T> (<t:${Math.round((new Date).getTime() / 1000) + muteTime}:R>)`))
`You will be unmuted at: <t:${Math.round((new Date).getTime() / 1000) + muteTime}:D> at <t:${Math.round((new Date).getTime() / 1000) + muteTime}:T> (<t:${Math.round((new Date).getTime() / 1000) + muteTime}:R>)`) +
(confirmation.components.appeal.response ? `You can appeal this here: <#${confirmation.components.appeal.response}>` : ``))
.setStatus("Danger")
],
components: [new MessageActionRow().addComponents(config.moderation.mute.text ? [new MessageButton()
@ -198,16 +205,16 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
.setTitle(`Mute`)
.setDescription("Something went wrong and the user was not muted")
.setStatus("Danger")
], components: []})
], components: []}) // TODO: make this clearer
if (dmd) await dm.delete()
return
}
try { await client.database.history.create("mute", interaction.guild.id, member.user, interaction.user, reason) } catch {}
let failed = (dmd == false && interaction.options.getString("notify") != "no")
let failed = (dmd == false && notify)
await interaction.editReply({embeds: [new EmojiEmbed()
.setEmoji(`PUNISH.MUTE.${failed ? "YELLOW" : "GREEN"}`)
.setTitle(`Mute`)
.setDescription("The member was muted" + (failed ? ", but could not be notified" : ""))
.setDescription("The member was muted" + (failed ? ", but could not be notified" : "") + (confirmation.components.appeal.response ? ` and an appeal ticket was opened in <#${confirmation.components.appeal.response}>` : ``))
.setStatus(failed ? "Warning" : "Success")
], components: []})
let data = {

@ -13,40 +13,43 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.setDescription("Changes a users nickname")
.addUserOption(option => option.setName("user").setDescription("The user to change").setRequired(true))
.addStringOption(option => option.setName("name").setDescription("The name to set | Leave blank to clear").setRequired(false))
.addStringOption(option => option.setName("notify").setDescription("If the user should get a message when their nickname is changed | Default: No").setRequired(false)
.addChoices([["Yes", "yes"], ["No", "no"]])
)
const callback = async (interaction: CommandInteraction): Promise<any> => {
const { renderUser } = client.logger
// TODO:[Modals] Replace this with a modal
let confirmation = await new confirmationMessage(interaction)
.setEmoji("PUNISH.NICKNAME.RED")
.setTitle("Nickname")
.setDescription(keyValueList({
"user": renderUser(interaction.options.getUser("user")),
"new nickname": `${interaction.options.getString("name") ? interaction.options.getString("name") : "*No nickname*"}`
})
+ `The user **will${interaction.options.getString("notify") == "yes" ? '' : ' not'}** be notified\n\n`
+ `Are you sure you want to ${interaction.options.getString("name") ? "change" : "clear"} <@!${(interaction.options.getMember("user") as GuildMember).id}>'s nickname?`)
.setColor("Danger")
.addCustomBoolean(
"Create appeal ticket", !(await areTicketsEnabled(interaction.guild.id)),
async () => await create(interaction.guild, interaction.options.getUser("user"), interaction.user, null),
"An appeal ticket will be created when Confirm is clicked")
.send()
let notify = true;
let confirmation;
while (true) {
confirmation = await new confirmationMessage(interaction)
.setEmoji("PUNISH.NICKNAME.RED")
.setTitle("Nickname")
.setDescription(keyValueList({
"user": renderUser(interaction.options.getUser("user")),
"new nickname": `${interaction.options.getString("name") ? interaction.options.getString("name") : "*No nickname*"}`
})
+ `The user **will${notify ? '' : ' not'}** be notified\n\n`
+ `Are you sure you want to ${interaction.options.getString("name") ? "change" : "clear"} <@!${(interaction.options.getMember("user") as GuildMember).id}>'s nickname?`)
.setColor("Danger")
.addCustomBoolean("notify", "Notify user", false, null, null, "ICONS.NOTIFY." + (notify ? "ON" : "OFF" ), notify)
.send(interaction.options.getString("name") !== null)
if (confirmation.cancelled) return
if (confirmation.success) break
if (confirmation.components) {
notify = confirmation.components.notify.active
}
}
if (confirmation.success) {
let dmd = false
let dm;
try {
if (interaction.options.getString("notify") == "yes") {
if (notify) {
dm = await (interaction.options.getMember("user") as GuildMember).send({
embeds: [new EmojiEmbed()
.setEmoji("PUNISH.NICKNAME.RED")
.setTitle("Nickname changed")
.setDescription(`Your nickname was ${interaction.options.getString("name") ? "changed" : "cleared"} in ${interaction.guild.name}.` +
(interaction.options.getString("name") ? ` it is now: ${interaction.options.getString("name")}` : "") + "\n\n" +
(confirmation.buttonClicked ? `You can appeal this here: <#${confirmation.response}>` : ``))
(confirmation.components.appeal.response ? `You can appeal this here: <#${confirmation.components.appeal.response}>` : ``))
.setStatus("Danger")
]
})
@ -61,8 +64,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
try { await client.database.history.create(
"nickname", interaction.guild.id, member.user, interaction.user,
null, before, nickname) } catch {}
// @ts-ignore
const { log, NucleusColors, entry, renderUser, renderDelta, getAuditLog } = client.logger
const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger
let data = {
meta: {
type: 'memberUpdate',
@ -94,11 +96,11 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
if (dmd) await dm.delete()
return
}
let failed = (dmd == false && interaction.options.getString("notify") == "yes")
let failed = (dmd == false && notify)
await interaction.editReply({embeds: [new EmojiEmbed()
.setEmoji(`PUNISH.NICKNAME.${failed ? "YELLOW" : "GREEN"}`)
.setTitle(`Nickname`)
.setDescription("The members nickname was changed" + (failed ? ", but was not notified" : "") + (confirmation.response ? ` and an appeal ticket was opened in <#${confirmation.response}>` : ``))
.setDescription("The members nickname was changed" + (failed ? ", but was not notified" : "") + (confirmation.components.appeal.response ? ` and an appeal ticket was opened in <#${confirmation.components.appeal.response}>` : ``))
.setStatus(failed ? "Warning" : "Success")
], components: []})
} else {

@ -132,7 +132,6 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
}
let attachmentObject;
try {
// @ts-ignore
const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger
let data = {
meta: {
@ -210,6 +209,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
}))
.setColor("Danger")
.send()
if (confirmation.cancelled) return
if (confirmation.success) {
let messages;
try {
@ -234,7 +234,6 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
}
let attachmentObject;
try {
// @ts-ignore
const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger
let data = {
meta: {

@ -31,6 +31,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
+ `Are you sure you want to set the slowmode in this channel?`)
.setColor("Danger")
.send()
if (confirmation.cancelled) return
if (confirmation.success) {
try {
(interaction.channel as TextChannel).setRateLimitPerUser(time)

@ -13,14 +13,12 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.setDescription("Kicks a user and deletes their messages")
.addUserOption(option => option.setName("user").setDescription("The user to softban").setRequired(true))
.addIntegerOption(option => option.setName("delete").setDescription("The days of messages to delete | Default: 0").setMinValue(0).setMaxValue(7).setRequired(false))
.addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are softbanned | Default: Yes").setRequired(false)
.addChoices([["Yes", "yes"], ["No", "no"]])
)
const callback = async (interaction: CommandInteraction): Promise<any> => {
const { renderUser } = client.logger
// TODO:[Modals] Replace this with a modal
let reason = null;
let notify = true;
let confirmation;
while (true) {
let confirmation = await new confirmationMessage(interaction)
@ -30,21 +28,26 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
"user": renderUser(interaction.options.getUser("user")),
"reason": reason ? ("\n> " + ((reason ?? "").replaceAll("\n", "\n> "))) : "*No reason provided*"
})
+ `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n`
+ `The user **will${notify ? '' : ' not'}** be notified\n`
+ `${addPlural(interaction.options.getInteger("delete") ? interaction.options.getInteger("delete") : 0, "day")} of messages will be deleted\n\n`
+ `Are you sure you want to softban <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
.setColor("Danger")
.addCustomBoolean("notify", "Notify user", false, null, null, "ICONS.NOTIFY." + (notify ? "ON" : "OFF" ), notify)
.addReasonButton(reason ?? "")
.send(reason !== null)
reason = reason ?? ""
if (confirmation.newReason === undefined) break
reason = confirmation.newReason
if (confirmation.cancelled) return
if (confirmation.success) break
if (confirmation.newReason) reason = confirmation.newReason
if (confirmation.components) {
notify = confirmation.components.notify.active
}
}
if (confirmation.success) {
let dmd = false;
let config = await client.database.guilds.read(interaction.guild.id);
try {
if (interaction.options.getString("notify") != "no") {
if (notify) {
await (interaction.options.getMember("user") as GuildMember).send({
embeds: [new EmojiEmbed()
.setEmoji("PUNISH.BAN.RED")
@ -78,7 +81,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
], components: []})
}
try { await client.database.history.create("softban", interaction.guild.id, member.user, reason) } catch {}
let failed = (dmd == false && interaction.options.getString("notify") != "no")
let failed = (dmd == false && notify)
await interaction.editReply({embeds: [new EmojiEmbed()
.setEmoji(`PUNISH.BAN.${failed ? "YELLOW" : "GREEN"}`)
.setTitle(`Softban`)

@ -36,12 +36,12 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
+ `Are you sure you want to unban <@${resolved.user.id}>?`)
.setColor("Danger")
.send()
if (confirmation.cancelled) return
if (confirmation.success) {
try {
await interaction.guild.members.unban(resolved.user as User, "Unban");
let member = (resolved.user as User)
try { await client.database.history.create("unban", interaction.guild.id, member, interaction.user) } catch {}
// @ts-ignore
const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger
let data = {
meta: {

@ -11,14 +11,12 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.setName("unmute")
.setDescription("Unmutes a user")
.addUserOption(option => option.setName("user").setDescription("The user to unmute").setRequired(true))
.addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are unmuted | Default: No").setRequired(false)
.addChoices([["Yes", "yes"], ["No", "no"]])
)
const callback = async (interaction: CommandInteraction): Promise<any> => {
const { log, NucleusColors, renderUser, entry, renderDelta } = client.logger
// TODO:[Modals] Replace this with a modal
let reason = null;
let notify = false;
let confirmation;
while (true) {
confirmation = await new confirmationMessage(interaction)
@ -28,20 +26,23 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
"user": renderUser(interaction.options.getUser("user")),
"reason": `\n> ${reason ? reason : "*No reason provided*"}`
})
+ `The user **will${interaction.options.getString("notify") === "yes" ? '' : ' not'}** be notified\n\n`
+ `The user **will${notify ? '' : ' not'}** be notified\n\n`
+ `Are you sure you want to unmute <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
.setColor("Danger")
.addReasonButton(reason ?? "")
.send(reason !== null)
reason = reason ?? ""
if (confirmation.newReason === undefined) break
reason = confirmation.newReason
if (confirmation.success) break
if (confirmation.newReason) reason = confirmation.newReason
if (confirmation.components) {
notify = confirmation.components.notify.active
}
}
if (confirmation.cancelled) return
if (confirmation.success) {
let dmd = false
let dm;
try {
if (interaction.options.getString("notify") != "no") {
if (notify) {
dm = await (interaction.options.getMember("user") as GuildMember).send({
embeds: [new EmojiEmbed()
.setEmoji("PUNISH.MUTE.GREEN")
@ -88,7 +89,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
}
}
log(data);
let failed = (dmd == false && interaction.options.getString("notify") != "no")
let failed = (dmd == false && notify)
await interaction.editReply({embeds: [new EmojiEmbed()
.setEmoji(`PUNISH.MUTE.${failed ? "YELLOW" : "GREEN"}`)
.setTitle(`Unmute`)

@ -1,4 +1,4 @@
import Discord, { CommandInteraction, GuildMember, MessageActionRow } from "discord.js";
import Discord, { CommandInteraction, GuildMember, MessageActionRow, MessageButton } from "discord.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { WrappedCheck } from "jshaiku";
import confirmationMessage from "../../utils/confirmationMessage.js";
@ -12,14 +12,13 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.setName("warn")
.setDescription("Warns a user")
.addUserOption(option => option.setName("user").setDescription("The user to warn").setRequired(true))
.addStringOption(option => option.setName("notify").setDescription("If the user should get a message when they are warned | Default: Yes").setRequired(false)
.addChoices([["Yes", "yes"], ["No", "no"]])
)
const callback = async (interaction: CommandInteraction): Promise<any> => {
const { log, NucleusColors, renderUser, entry } = client.logger
// TODO:[Modals] Replace this with a modal
let reason = null;
let notify = true;
let createAppealTicket = false;
let confirmation;
while (true) {
confirmation = await new confirmationMessage(interaction)
@ -29,44 +28,52 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
"user": renderUser(interaction.options.getUser("user")),
"reason": reason ? ("\n> " + ((reason ?? "").replaceAll("\n", "\n> "))) : "*No reason provided*"
})
+ `The user **will${interaction.options.getString("notify") === "no" ? ' not' : ''}** be notified\n\n`
+ `The user **will${notify ? '' : ' not'}** be notified\n\n`
+ `Are you sure you want to warn <@!${(interaction.options.getMember("user") as GuildMember).id}>?`)
.setColor("Danger")
.addCustomBoolean(
"Create appeal ticket", !(await areTicketsEnabled(interaction.guild.id)),
"appeal", "Create appeal ticket", !(await areTicketsEnabled(interaction.guild.id)),
async () => await create(interaction.guild, interaction.options.getUser("user"), interaction.user, reason),
"An appeal ticket will be created when Confirm is clicked")
.addReasonButton(reason)
"An appeal ticket will be created when Confirm is clicked", "CONTROL.TICKET", createAppealTicket)
.addCustomBoolean("notify", "Notify user", false, null, null, "ICONS.NOTIFY." + (notify ? "ON" : "OFF" ), notify)
.addReasonButton(reason ?? "")
.send(reason !== null)
reason = reason ?? ""
if (confirmation.newReason === undefined) break
reason = confirmation.newReason
if (confirmation.cancelled) return
if (confirmation.success) break
if (confirmation.newReason) reason = confirmation.newReason
if (confirmation.components) {
notify = confirmation.components.notify.active
createAppealTicket = confirmation.components.appeal.active
}
}
if (confirmation.success) {
let dmd = false
try {
if (interaction.options.getString("notify") != "no") {
if (notify) {
const config = await client.database.guilds.read(interaction.guild.id)
await (interaction.options.getMember("user") as GuildMember).send({
embeds: [new EmojiEmbed()
.setEmoji("PUNISH.WARN.RED")
.setTitle("Warned")
.setDescription(`You have been warned in ${interaction.guild.name}` +
(reason ? ` for:\n> ${reason}` : ".") + "\n\n" +
(confirmation.buttonClicked ? `You can appeal this here ticket: <#${confirmation.response}>` : ``))
(confirmation.components.appeal.response ? `You can appeal this here ticket: <#${confirmation.components.appeal.response}>` : ``))
.setStatus("Danger")
]
.setFooter({
text: config.moderation.warn.text ? "The button below is set by the server admins. Do not enter any passwords or other account details on the linked site." : "",
iconURL: "https://cdn.discordapp.com/emojis/952295894370369587.webp?size=128&quality=lossless"
})
],
components: config.moderation.warn.text ? [new MessageActionRow().addComponents([new MessageButton()
.setStyle("LINK")
.setLabel(config.moderation.warn.text)
.setURL(config.moderation.warn.link)
])] : []
})
dmd = true
}
} catch {
await interaction.editReply({embeds: [new EmojiEmbed()
.setEmoji("PUNISH.WARN.RED")
.setTitle(`Warn`)
.setDescription("Something went wrong and the user was not warned")
.setStatus("Danger")
], components: []})
}
} catch {}
let data = {
meta:{
type: 'memberWarn',
@ -91,12 +98,12 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
interaction.user, reason
)} catch {}
log(data);
let failed = (dmd == false && interaction.options.getString("notify") != "no")
let failed = (dmd == false && notify)
if (!failed) {
await interaction.editReply({embeds: [new EmojiEmbed()
.setEmoji(`PUNISH.WARN.GREEN`)
.setTitle(`Warn`)
.setDescription("The user was warned" + (confirmation.response ? ` and an appeal ticket was opened in <#${confirmation.response}>` : ``))
.setDescription("The user was warned" + (confirmation.components.appeal.response ? ` and an appeal ticket was opened in <#${confirmation.components.appeal.response}>` : ``))
.setStatus("Success")
], components: []})
} else {
@ -118,7 +125,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
.setStyle("SECONDARY")
.setDisabled((interaction.options.getMember("user") as GuildMember).permissionsIn(interaction.channel as Discord.TextChannel).has("VIEW_CHANNEL") === false),
])
],
]
})
let component;
try {

@ -12,7 +12,6 @@ const command = (builder: SlashCommandSubcommandBuilder) =>
.addStringOption(option => option.setName("suggestion").setDescription("The suggestion to send").setRequired(true))
const callback = async (interaction: CommandInteraction): Promise<any> => {
// @ts-ignore
const { renderUser } = client.logger
let suggestion = interaction.options.getString("suggestion");
let confirmation = await new confirmationMessage(interaction)
@ -23,6 +22,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
.setColor("Danger")
.setInverted(true)
.send()
if (confirmation.cancelled) return
if (confirmation.success) {
await (client.channels.cache.get('955161206459600976') as Discord.TextChannel).send({
embeds: [

@ -28,10 +28,10 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
.setDescription(keyValueList({
"user": renderUser(interaction.options.getUser("user")),
"role": renderRole(interaction.options.getRole("role"))
})
+ `\nAre you sure you want to ${action == "give" ? "give the role to" : "remove the role from"} ${interaction.options.getUser("user")}?`)
}) + `\nAre you sure you want to ${action == "give" ? "give the role to" : "remove the role from"} ${interaction.options.getUser("user")}?`)
.setColor("Danger")
.send()
if (confirmation.cancelled) return
if (confirmation.success) {
try {
let member = interaction.options.getMember("user") as GuildMember

@ -50,6 +50,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
.setColor("Warning")
.setInverted(true)
.send(true)
if (confirmation.cancelled) return
if (confirmation.success) {
try {
await client.database.guilds.write(interaction.guild.id, {"logging.logs.channel": channel.id})

@ -96,6 +96,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
+ `Are you sure you want to **${interaction.options.getString("action") == "add" ? "add" : "remove"}** these to the ignore list?`)
.setColor("Warning")
.send(true)
if (confirmation.cancelled) return
if (confirmation.success) {
let data = client.database.guilds.read(interaction.guild.id)
if (channel) data.logging.logs.ignore.channels.concat([channel.id])

@ -1,153 +0,0 @@
import { ChannelType } from 'discord-api-types';
import Discord, { CommandInteraction, MessageActionRow, MessageButton } from "discord.js";
import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
import confirmationMessage from "../../utils/confirmationMessage.js";
import getEmojiByName from "../../utils/getEmojiByName.js";
import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
import { WrappedCheck } from "jshaiku";
import client from "../../utils/client.js";
const command = (builder: SlashCommandSubcommandBuilder) =>
builder
.setName("staff")
.setDescription("Settings for the staff notifications channel")
.addChannelOption(option => option.setName("channel").setDescription("The channel to set the staff notifications channel to").addChannelTypes([
ChannelType.GuildNews, ChannelType.GuildText
]).setRequired(false))
const callback = async (interaction: CommandInteraction): Promise<any> => {
let m;
m = await interaction.reply({embeds: [new EmojiEmbed()
.setTitle("Loading")
.setStatus("Danger")
.setEmoji("NUCLEUS.LOADING")
], ephemeral: true, fetchReply: true});
if (interaction.options.getChannel("channel")) {
let channel
try {
channel = interaction.options.getChannel("channel")
} catch {
return await interaction.editReply({embeds: [new EmojiEmbed()
.setEmoji("CHANNEL.TEXT.DELETE")
.setTitle("Staff Notifications Channel")
.setDescription("The channel you provided is not a valid channel")
.setStatus("Danger")
]})
}
channel = channel as Discord.TextChannel
if (channel.guild.id != interaction.guild.id) {
return interaction.editReply({embeds: [new EmojiEmbed()
.setTitle("Staff Notifications Channel")
.setDescription(`You must choose a channel in this server`)
.setStatus("Danger")
.setEmoji("CHANNEL.TEXT.DELETE")
]});
}
let confirmation = await new confirmationMessage(interaction)
.setEmoji("CHANNEL.TEXT.EDIT")
.setTitle("Staff Notifications Channel")
.setDescription(
`This will be the channel all notifications, updates, user reports etc. will be sent to.\n\n` +
`Are you sure you want to set the staff notifications channel to <#${channel.id}>?`
)
.setColor("Warning")
.setInverted(true)
.send(true)
if (confirmation.success) {
try {
await client.database.guilds.write(interaction.guild.id, {"logging.staff.channel": channel.id})
const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger
try {
let data = {
meta:{
type: 'logIgnoreUpdated',
displayName: 'Staff Notifications Channel Updated',
calculateType: 'nucleusSettingsUpdated',
color: NucleusColors.yellow,
emoji: "CHANNEL.TEXT.EDIT",
timestamp: new Date().getTime()
},
list: {
memberId: entry(interaction.user.id, `\`${interaction.user.id}\``),
changedBy: entry(interaction.user.id, renderUser(interaction.user)),
channel: entry(channel.id, renderChannel(channel)),
},
hidden: {
guild: interaction.guild.id
}
}
log(data);
} catch {}
} catch (e) {
return interaction.editReply({embeds: [new EmojiEmbed()
.setTitle("Staff Notifications Channel")
.setDescription(`Something went wrong and the staff notifications channel could not be set`)
.setStatus("Danger")
.setEmoji("CHANNEL.TEXT.DELETE")
], components: []});
}
} else {
return interaction.editReply({embeds: [new EmojiEmbed()
.setTitle("Staff Notifications Channel")
.setDescription(`No changes were made`)
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
], components: []});
}
}
let clicks = 0;
let data = await client.database.guilds.read(interaction.guild.id);
let channel = data.logging.staff.channel;
while (true) {
await interaction.editReply({embeds: [new EmojiEmbed()
.setTitle("Staff Notifications channel")
.setDescription(channel ? `Your staff notifications channel is currently set to <#${channel}>` : "This server does not have a staff notifications channel")
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
], components: [new MessageActionRow().addComponents([new MessageButton()
.setCustomId("clear")
.setLabel(clicks ? "Click again to confirm" : "Reset channel")
.setEmoji(getEmojiByName(clicks ? "TICKETS.ISSUE" : "CONTROL.CROSS", "id"))
.setStyle("DANGER")
.setDisabled(!channel)
])]});
let i;
try {
i = await m.awaitMessageComponent({time: 300000});
} catch(e) { break }
i.deferUpdate()
if (i.component.customId == "clear") {
clicks += 1;
if (clicks == 2) {
clicks = 0;
await client.database.guilds.write(interaction.guild.id, {}, ["logging.staff.channel"])
channel = undefined;
}
} else {
break
}
}
await interaction.editReply({embeds: [new EmojiEmbed()
.setTitle("Staff Notifications channel")
.setDescription(channel ? `Your staff notifications channel is currently set to <#${channel}>` : "This server does not have a staff notifications channel")
.setStatus("Success")
.setEmoji("CHANNEL.TEXT.CREATE")
.setFooter({text: "Message closed"})
], components: [new MessageActionRow().addComponents([new MessageButton()
.setCustomId("clear")
.setLabel("Clear")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
.setStyle("SECONDARY")
.setDisabled(true)
])]});
}
const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
let member = (interaction.member as Discord.GuildMember)
if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the Manage Server permission to use this command"
return true;
}
export { command };
export { callback };
export { check };

@ -109,6 +109,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
.setColor("Warning")
.setInverted(true)
.send(true)
if (confirmation.cancelled) return
if (confirmation.success) {
let toUpdate = {}
if (options.enabled !== null) toUpdate["tickets.enabled"] = options.enabled
@ -345,8 +346,7 @@ async function manageTypes(interaction, data, m) {
}
} else if (i.component.customId == "addType") {
await i.showModal(new Discord.Modal().setCustomId("modal").setTitle("Enter a name for the new type").addComponents(
// @ts-ignore
new MessageActionRow().addComponents(new TextInputComponent()
new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
.setCustomId("type")
.setLabel("Name")
.setMaxLength(100)

@ -47,6 +47,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
.setColor("Warning")
.setInverted(true)
.send(true)
if (confirmation.cancelled) return
if (confirmation.success) {
try {
await client.database.guilds.write(interaction.guild.id, {"verify.role": role.id, "verify.enabled": true});

@ -1,21 +1,58 @@
import { CommandInteraction } from "discord.js";
import { AutocompleteInteraction, CommandInteraction, MessageActionRow, MessageButton } from "discord.js";
import { SlashCommandBuilder } from "@discordjs/builders";
import { WrappedCheck } from "jshaiku";
import { callback as statsChannelAdd } from '../reflex/statsChannelAdd.js';
import client from "../utils/client.js"
import EmojiEmbed from "../utils/generateEmojiEmbed.js";
const command = new SlashCommandBuilder()
.setName("tag")
.setDescription("Get and manage the servers tags")
.addStringOption(o => o.setName("tag").setDescription("The tag to get").setAutocomplete(true).setRequired(true))
const callback = async (interaction: CommandInteraction) => {
const config = await client.database.guilds.read(interaction.guild.id)
const tags = config.getKey("tags")
const tag = tags[interaction.options.getString("tag")]
if (!tag) {
return await interaction.reply({embeds: [new EmojiEmbed()
.setTitle("Tag")
.setDescription(`Tag \`${interaction.options.getString("tag")}\` does not exist`)
.setEmoji("PUNISH.NICKNAME.RED")
.setStatus("Danger")
], ephemeral: true})
}
let url = ""
let components = []
if (tag.match(/^(http|https):\/\/[^ "]+$/)) {
url = tag
components = [new MessageActionRow().addComponents([new MessageButton()
.setLabel("Open")
.setURL(url)
.setStyle("LINK")
])]
}
return await interaction.reply({embeds: [new EmojiEmbed()
.setTitle(interaction.options.getString("tag"))
.setDescription(tag)
.setEmoji("PUNISH.NICKNAME.GREEN")
.setStatus("Success")
.setImage(url)
], components: components, ephemeral: true})
const callback = (interaction: CommandInteraction) => {
interaction.reply("This command is not yet finished [tag]");
}
const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
return true;
}
const autocomplete = async (interaction: AutocompleteInteraction): Promise<string[]> => {
if (!interaction.guild) return [];
const config = await client.database.guilds.read(interaction.guild.id)
const tags = Object.keys(config.getKey("tags"));
return tags
}
export { command };
export { callback };
export { check };
export { autocomplete };

@ -52,6 +52,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
.setColor("Warning")
.setInverted(true)
.send()
if (confirmation.cancelled) return
if (!confirmation) return await interaction.editReply({embeds: [new EmojiEmbed()
.setTitle("Tag Create")
.setDescription("No changes were made")

@ -32,6 +32,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
.setColor("Warning")
.setInverted(true)
.send()
if (confirmation.cancelled) return
if (!confirmation) return await interaction.editReply({embeds: [new EmojiEmbed()
.setTitle("Tag Delete")
.setDescription("No changes were made")
@ -39,9 +40,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
.setEmoji("PUNISH.NICKNAME.GREEN")
]});
try {
data = await client.database.guilds.read(interaction.guild.id);
delete data.tags[name];
await client.database.guilds.write(interaction.guild.id, {tags: data});
await client.database.guilds.write(interaction.guild.id, null, [`tags.${name}`]);
} catch (e) {
return await interaction.editReply({embeds: [new EmojiEmbed()
.setTitle("Tag Delete")

@ -60,6 +60,7 @@ const callback = async (interaction: CommandInteraction): Promise<any> => {
.setColor("Warning")
.setInverted(true)
.send()
if (confirmation.cancelled) return
if (!confirmation) return await interaction.editReply({embeds: [new EmojiEmbed()
.setTitle("Tag Edit")
.setDescription("No changes were made")

@ -52,7 +52,7 @@
"channel": null,
"message": null
},
"stats": [],
"stats": {},
"logging": {
"logs": {
"enabled": true,

@ -22,6 +22,10 @@
"FILTER": "990242059451514902",
"ATTACHMENT": "997570687193587812",
"LOGGING": "999613304446144562",
"NOTIFY": {
"ON": "1000726394579464232",
"OFF": "1000726363495477368"
},
"OPP": {
"ADD": "837355918831124500",
"REMOVE": "837355918420869162"

@ -1,5 +1,5 @@
import { purgeByUser } from '../actions/tickets/delete.js';
import { callback as statsChannelRemove } from '../reflex/statsChannelRemove.js';
import { callback as statsChannelRemove } from '../reflex/statsChannelUpdate.js';
export const event = 'guildBanAdd';

@ -1,6 +1,6 @@
import humanizeDuration from 'humanize-duration';
import { purgeByUser } from '../actions/tickets/delete.js';
import { callback as statsChannelRemove } from '../reflex/statsChannelRemove.js';
import { callback as statsChannelRemove } from '../reflex/statsChannelUpdate.js';
export const event = 'guildBanRemove';

@ -3,18 +3,43 @@ import verify from "../reflex/verify.js";
import create from "../actions/tickets/create.js";
import close from "../actions/tickets/delete.js";
import createTranscript from "../premium/createTranscript.js";
import Fuse from "fuse.js";
import { autocomplete as tagAutocomplete } from "../commands/tag.js"
export const event = 'interactionCreate';
function getAutocomplete(typed: string, options: string[]): object[] {
options = options.filter(option => option.length <= 100) // thanks discord. 6000 character limit on slash command inputs but only 100 for autocomplete.
if (!typed) return options.slice(0, 25).sort().map(option => ({name: option, value: option}))
const fuse = new Fuse(options, {useExtendedSearch: true, findAllMatches: true, minMatchCharLength: 0}).search(typed)
return fuse.slice(0, 25).map(option => ({name: option.item, value: option.item}))
}
const validReplacements = ["serverName", "memberCount", "memberCount:bots", "memberCount:humans"]
function generateStatsChannelAutocomplete(typed) {
let autocompletions = []
const beforeLastOpenBracket = typed.match(/(.*){[^{}]{0,15}$/)
if (beforeLastOpenBracket !== null) { for (let replacement of validReplacements) { autocompletions.push(`${beforeLastOpenBracket[1]} {${replacement}}`) } }
else { for (let replacement of validReplacements) { autocompletions.push(`${typed} {${replacement}}`) } }
return getAutocomplete(typed, autocompletions)
}
async function interactionCreate(interaction) {
if (interaction.componentType === "BUTTON") {
if (interaction.customId === "rolemenu") return await roleMenu(interaction)
if (interaction.customId === "verifybutton") return verify(interaction)
if (interaction.customId === "createticket") return create(interaction)
if (interaction.customId === "closeticket") return close(interaction)
if (interaction.customId === "createtranscript") return createTranscript(interaction)
switch (interaction.customId) {
case "rolemenu": { return await roleMenu(interaction) }
case "verifybutton": { return verify(interaction) }
case "createticket": { return create(interaction) }
case "closeticket": { return close(interaction) }
case "createtranscript": { return createTranscript(interaction) }
}
} else if (interaction.componentType === "MESSAGE_COMPONENT") {
console.table(interaction)
} else if (interaction.type === "APPLICATION_COMMAND_AUTOCOMPLETE") {
switch (`${interaction.commandName} ${interaction.options.getSubcommandGroup(false)} ${interaction.options.getSubcommand(false)}`) {
case `tag null null`: { return interaction.respond(getAutocomplete(interaction.options.getString("tag"), (await tagAutocomplete(interaction)))) }
case `settings stats set`: { return interaction.respond(generateStatsChannelAutocomplete(interaction.options.getString("name"))) }
}
}
}

@ -1,4 +1,4 @@
import { callback as statsChannelAdd } from '../reflex/statsChannelAdd.js';
import { callback as statsChannelAdd } from '../reflex/statsChannelUpdate.js';
import { callback as welcome } from '../reflex/welcome.js';
import log from '../utils/log.js';
import client from '../utils/client.js';
@ -7,7 +7,6 @@ export const event = 'guildMemberAdd'
export async function callback(_, member) {
try { welcome(_, member); } catch {}
try { statsChannelAdd(_, member); } catch {}
try {
const { log, NucleusColors, entry, renderUser, renderDelta } = member.client.logger
try { await client.database.history.create("join", member.guild.id, member.user, null, null) } catch {}
@ -33,4 +32,5 @@ export async function callback(_, member) {
}
log(data);
} catch {}
try { statsChannelAdd(_, member, ); } catch {}
}

@ -1,11 +1,10 @@
import humanizeDuration from 'humanize-duration';
import { purgeByUser } from '../actions/tickets/delete.js';
import { callback as statsChannelRemove } from '../reflex/statsChannelRemove.js';
import { callback as statsChannelRemove } from '../reflex/statsChannelUpdate.js';
export const event = 'guildMemberRemove'
export async function callback(client, member) {
try { await statsChannelRemove(client, member); } catch {}
try { purgeByUser(member.id, member.guild); } catch {}
try {
const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta } = member.client.logger
@ -72,4 +71,5 @@ export async function callback(client, member) {
}
log(data);
} catch (e) { console.log(e) }
try { await statsChannelRemove(client, member); } catch {}
}

@ -1,7 +1,6 @@
import { LinkCheck, MalwareCheck, NSFWCheck, SizeCheck, TestString, TestImage } from '../reflex/scanners.js'
import logAttachment from '../premium/attachmentLogs.js'
import createLogException from '../utils/createLogException.js'
import { capitalize } from '../utils/generateKeyValueList.js'
import getEmojiByName from '../utils/getEmojiByName.js'
export const event = 'messageCreate'
@ -19,7 +18,7 @@ export async function callback(client, message) {
let config = await client.memory.readGuildInfo(message.guild.id);
const filter = getEmojiByName("ICONS.FILTER")
let attachmentJump = ""
if (config.logging.attachments.saved[message.channel.id + message.id]) { attachmentJump = ` [[View attachments]](${config})` }
if (config.logging.attachments.saved[message.channel.id + message.id]) { attachmentJump = ` [[View attachments]](${config.logging.attachments.saved[message.channel.id + message.id]})` }
let list = {
messageId: entry(message.id, `\`${message.id}\``),
sentBy: entry(message.author.id, renderUser(message.author)),
@ -64,7 +63,7 @@ export async function callback(client, message) {
}
if (fileNames.files.length > 0) {
fileNames.files.forEach(async element => {
for (let element of fileNames.files) {
if(!message) return;
let url = element.url ? element.url : element.local
if (url != undefined) {
@ -173,7 +172,7 @@ export async function callback(client, message) {
}
}
}
});
};
}
if(!message) return;

@ -14,6 +14,7 @@ export async function callback(client, message) {
let content = message.cleanContent
content.replace(`\``, `\\\``)
if (content.length > 256) content = content.substring(0, 253) + '...'
let attachments = message.attachments.size + (message.content.match(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi) ?? []).length
let attachmentJump = ""
let config = (await client.database.guilds.read(message.guild.id)).logging.attachments.saved[message.channel.id + message.id];
if (config) { attachmentJump = ` [[View attachments]](${config})` }
@ -35,7 +36,7 @@ export async function callback(client, message) {
sentIn: entry(message.channel.id, renderChannel(message.channel)),
deleted: entry(new Date().getTime(), renderDelta(new Date().getTime())),
mentions: message.mentions.users.size,
attachments: entry(message.attachments.size, message.attachments.size + attachmentJump),
attachments: entry(attachments, attachments + attachmentJump),
repliedTo: entry(
message.reference.messageId || null,
message.reference.messageId ? `[[Jump to message]](https://discord.com/channels/${message.guild.id}/${message.channel.id}/${message.reference.messageId})` : "None"

@ -11,6 +11,9 @@ await client.registerEventsIn("./events");
client.on("ready", () => {
runServer(client);
});
process.on("unhandledRejection", (err) => {
console.error(err);
});
client.logger = new Logger()
client.verify = {}

@ -4,14 +4,16 @@ import { writeFileSync } from 'fs'
import generateFileName from '../utils/temp/generateFileName.js'
import Tesseract from 'node-tesseract-ocr';
interface NSFWSchema { nsfw: boolean }
interface MalwareSchema { safe: boolean }
export async function testNSFW(link: string): Promise<JSON> {
export async function testNSFW(link: string): Promise<NSFWSchema> {
let p = await saveAttachment(link)
let result = await us.nsfw.file(p)
return result
}
export async function testMalware(link: string): Promise<JSON> {
export async function testMalware(link: string): Promise<MalwareSchema> {
let p = await saveAttachment(link)
let result = await us.malware.file(p)
return result
@ -24,7 +26,7 @@ export async function saveAttachment(link): Promise<string> {
return fileName
}
export async function testLink(link: string): Promise<JSON> {
export async function testLink(link: string): Promise<unknown> {
return await us.link.scan(link)
}
@ -75,7 +77,6 @@ export async function LinkCheck(message): Promise<string[]> {
export async function NSFWCheck(element): Promise<boolean> {
try {
let test = (await testNSFW(element))
//@ts-ignore
return test.nsfw
} catch {
return false
@ -90,8 +91,7 @@ export async function SizeCheck(element): Promise<boolean> {
export async function MalwareCheck(element): Promise<boolean> {
try {
//@ts-ignore
return (await scan.testMalware(element)).safe
return (await testMalware(element)).safe
} catch {
return true
}

@ -1,33 +0,0 @@
import convertCurlyBracketString from '../utils/convertCurlyBracketString.js'
import singleNotify from '../utils/singleNotify.js';
import client from '../utils/client.js';
export async function callback(_, member) {
let config = await client.database.guilds.read(member.guild.id);
config.stats.forEach(async element => {
if (element.enabled) {
let string = element.text
if (!string) return
string = await convertCurlyBracketString(string, member.id, member.displayName, member.guild.name, member.guild.members)
let channel;
try {
channel = await member.client.channels.fetch(element.channel)
} catch (error) { channel = null }
if (!channel) {
return singleNotify(
"statsChannelDeleted",
member.guild.id,
"One or more of your stats channels have been deleted. Please open the settings menu to change this.",
"Critical"
)
}
if (channel.guild.id !== member.guild.id) return
try {
await channel.edit({ name: string })
} catch (err) {
console.error(err)
}
}
});
}

@ -1,30 +0,0 @@
import client from '../utils/client.js';
import convertCurlyBracketString from '../utils/convertCurlyBracketString.js'
import singleNotify from '../utils/singleNotify.js';
export async function callback(_, member) {
let config = await client.database.guilds.read(member.guild.id);
config.stats.forEach(async element => {
if (element.enabled) {
let string = element.text
if (!string) return
string = await convertCurlyBracketString(string, member.id, member.displayName, member.guild.name, member.guild.members)
let channel;
try {
channel = await member.client.channels.fetch(element.channel)
} catch { channel = null }
if (!channel) return singleNotify(
"statsChannelDeleted",
member.guild.id,
"One or more of your stats channels have been deleted. Please open the settings menu to change this.",
"Critical"
)
try {
await channel.edit({ name: string })
} catch (err) {
console.error(err)
}
}
});
}

@ -3,20 +3,24 @@ import { modalInteractionCollector } from "./dualCollector.js";
import EmojiEmbed from "./generateEmojiEmbed.js"
import getEmojiByName from "./getEmojiByName.js";
interface CustomBoolean<T> {
title: string;
disabled: boolean;
value: string | null;
emoji: string | null;
active: boolean;
onClick: () => Promise<T>;
response: T | null;
}
class confirmationMessage {
interaction: CommandInteraction;
title: string = "";
emoji: string = "";
description: string = "";
color: string = "";
customCallback: () => any = () => {};
customButtonTitle: string;
customButtonDisabled: boolean;
customCallbackString: string = "";
customCallbackClicked: boolean = false;
customCallbackResponse: any = null;
customBoolean: () => any = () => {}; // allow multiple booleans
customBooleanClicked: boolean = null;
customButtons: {[index:string]: CustomBoolean<unknown>} = {};
inverted: boolean = false;
reason: string | null = null;
@ -29,21 +33,15 @@ class confirmationMessage {
setDescription(description: string) { this.description = description; return this }
setColor(color: string) { this.color = color; return this }
setInverted(inverted: boolean) { this.inverted = inverted; return this }
addCustomCallback(title: string, disabled: boolean, callback: () => any, callbackClicked: string) {
if (this.customButtonTitle) return this
this.customButtonTitle = title;
this.customButtonDisabled = disabled;
this.customCallback = callback;
this.customCallbackString = callbackClicked;
return this;
}
addCustomBoolean(title: string, disabled: boolean, callback: () => any, callbackClicked: string) {
if (this.customButtonTitle) return this
this.customButtonTitle = title;
this.customButtonDisabled = disabled;
this.customBoolean = callback;
this.customCallbackString = callbackClicked;
this.customBooleanClicked = false;
addCustomBoolean(customId: string, title: string, disabled: boolean, callback: () => Promise<unknown> | null, callbackClicked: string | null, emoji?: string, initial?: boolean) { this.customButtons[customId] = {
title: title,
disabled: disabled,
value: callbackClicked,
emoji: emoji,
active: initial ?? false,
onClick: callback ?? (() => null),
response: null,
}
return this;
}
addReasonButton(reason: string) {
@ -52,92 +50,80 @@ class confirmationMessage {
}
async send(editOnly?: boolean) {
while (true) {
let fullComponents = [
new Discord.MessageButton()
.setCustomId("yes")
.setLabel("Confirm")
.setStyle(this.inverted ? "SUCCESS" : "DANGER")
.setEmoji(getEmojiByName("CONTROL.TICK", "id")),
new Discord.MessageButton()
.setCustomId("no")
.setLabel("Cancel")
.setStyle("SECONDARY")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
]
Object.entries(this.customButtons).forEach(([k, v]) => {
fullComponents.push(new Discord.MessageButton()
.setCustomId(k)
.setLabel(v.title)
.setStyle(v.active ? "SUCCESS" : "PRIMARY")
.setEmoji(getEmojiByName(v.emoji, "id"))
.setDisabled(v.disabled))
})
if (this.reason !== null) fullComponents.push(new Discord.MessageButton()
.setCustomId("reason")
.setLabel(`Edit Reason`)
.setStyle("PRIMARY")
.setEmoji(getEmojiByName("ICONS.EDIT", "id"))
.setDisabled(false)
)
let components = []
for (let i = 0; i < fullComponents.length; i += 5) {
components.push(new MessageActionRow().addComponents(fullComponents.slice(i, i + 5)));
}
let object = {
embeds: [
new EmojiEmbed()
.setEmoji(this.emoji)
.setTitle(this.title)
.setDescription(this.description)
.setDescription(this.description + "\n\n" + Object.values(this.customButtons).map(v => {
if (v.value === null) return "";
return v.active ? `*${v.value}*\n` : "";
}).join(""))
.setStatus(this.color)
.setFooter({text: (this.customBooleanClicked ?? this.customCallbackClicked) ? this.customCallbackString : ""})
],
components: [
new MessageActionRow().addComponents([
new Discord.MessageButton()
.setCustomId("yes")
.setLabel("Confirm")
.setStyle(this.inverted ? "SUCCESS" : "DANGER")
.setEmoji(getEmojiByName("CONTROL.TICK", "id")),
new Discord.MessageButton()
.setCustomId("no")
.setLabel("Cancel")
.setStyle("SECONDARY")
.setEmoji(getEmojiByName("CONTROL.CROSS", "id"))
].concat(this.customButtonTitle ? [new Discord.MessageButton()
.setCustomId("custom")
.setLabel(this.customButtonTitle)
.setStyle(this.customBooleanClicked !== null ?
( this.customBooleanClicked ? "SUCCESS" : "PRIMARY" ) :
"PRIMARY"
)
.setDisabled(this.customButtonDisabled)
.setEmoji(getEmojiByName("CONTROL.TICKET", "id"))
] : [])
.concat(this.reason !== null ? [new Discord.MessageButton()
.setCustomId("reason")
.setLabel(`Edit Reason`)
.setStyle("PRIMARY")
.setEmoji(getEmojiByName("ICONS.EDIT", "id"))
] : []))
],
components: components,
ephemeral: true,
fetchReply: true
}
let m;
if ( editOnly ) {
m = await this.interaction.editReply(object);
} else {
m = await this.interaction.reply(object)
}
try {
if ( editOnly ) {
m = await this.interaction.editReply(object);
} else {
m = await this.interaction.reply(object)
}
} catch { return { cancelled: true } }
let component;
try {
component = await (m as Message).awaitMessageComponent({filter: (m) => m.user.id === this.interaction.user.id, time: 300000});
} catch (e) {
return {
success: false,
buttonClicked: this.customBooleanClicked ?? this.customCallbackClicked,
response: this.customCallbackResponse
};
return { success: false, components: this.customButtons };
}
if (component.customId === "yes") {
component.deferUpdate();
if (this.customBooleanClicked === true) this.customCallbackResponse = await this.customBoolean();
return {
success: true,
buttonClicked: this.customBooleanClicked ?? this.customCallbackClicked,
response: this.customCallbackResponse
for (let [k, v] of Object.entries(this.customButtons)) {
if (!v.active) continue
try { v.response = await v.onClick(); }
catch (e) { console.log(e) }
};
return { success: true, components: this.customButtons };
} else if (component.customId === "no") {
component.deferUpdate();
return {
success: false,
buttonClicked: this.customBooleanClicked ?? this.customCallbackClicked,
response: this.customCallbackResponse
};
} else if (component.customId === "custom") {
component.deferUpdate();
if (this.customBooleanClicked !== null) {
this.customBooleanClicked = !this.customBooleanClicked;
} else {
this.customCallbackResponse = await this.customCallback();
this.customCallbackClicked = true;
this.customButtonDisabled = true;
}
editOnly = true;
return { success: false, components: this.customButtons };
} else if (component.customId === "reason") {
await component.showModal(new Discord.Modal().setCustomId("modal").setTitle(`Editing reason`).addComponents(
// @ts-ignore
new MessageActionRow().addComponents(new TextInputComponent()
new MessageActionRow<TextInputComponent>().addComponents(new TextInputComponent()
.setCustomId("reason")
.setLabel("Reason")
.setMaxLength(2000)
@ -163,10 +149,13 @@ class confirmationMessage {
let out;
try {
out = await modalInteractionCollector(m, (m) => m.channel.id == this.interaction.channel.id, (m) => m.customId == "reason")
} catch (e) { continue }
if (out.fields) {
return {newReason: out.fields.getTextInputValue("reason") ?? ""};
} else { return { newReason: this.reason } }
} catch (e) { return {} }
if (out.fields) { return { newReason: out.fields.getTextInputValue("reason") ?? "" }; }
else { return { newReason: this.reason } }
} else {
component.deferUpdate();
this.customButtons[component.customId].active = !this.customButtons[component.customId].active;
return { components: this.customButtons };
}
}
}

@ -2,12 +2,12 @@ async function convertCurlyBracketString(str, memberID, memberName, serverName,
let memberCount = (await members.fetch()).size
let bots = (await members.fetch()).filter(m => m.user.bot).size
str = str
.replace("{@}", `<@${memberID}>`)
.replace("{server}", `${serverName}`)
.replace("{name}", `${memberName}`)
.replace("{count}", `${memberCount}`)
.replace("{count:bots}", `${bots}`)
.replace("{count:humans}", `${memberCount - bots}`);
.replace("{member:mention}", memberID ? `<@${memberID}>` : "{member:mention}")
.replace("{member:name}", memberName ? `${memberName}` : "{member:name}")
.replace("{serverName}", serverName ? `${serverName}` : "{serverName}")
.replace("{memberCount}", memberCount ? `${memberCount}` : "{memberCount}")
.replace("{memberCount:bots}", bots ? `${bots}` : "{memberCount:bots}")
.replace("{memberCount:humans}", (memberCount && bots) ? `${memberCount - bots}` : "{memberCount:humans}");
return str
}

@ -63,8 +63,13 @@ export class Guilds {
}
}
async remove(guild: string, key: string, value: any) {
if (Array.isArray(value)) {
async remove(guild: string, key: string, value: any, innerKey?: string) {
if (innerKey) {
await this.guilds.updateOne({ id: guild }, {
$pull: { [key]: { [innerKey]: { $eq: value } } }
}, { upsert: true });
}
else if (Array.isArray(value)) {
await this.guilds.updateOne({ id: guild }, {
$pullAll: { [key]: value }
}, { upsert: true });
@ -200,11 +205,7 @@ export interface GuildConfig {
channel: string | null,
message: string | null,
}
stats: {
enabled: boolean,
channel: string | null,
text: string | null,
}[]
stats: {}
logging: {
logs: {
enabled: boolean,

Loading…
Cancel
Save