stats channels
diff --git a/TODO.json b/TODO.json
index b4b2524..d95afcb 100644
--- a/TODO.json
+++ b/TODO.json
@@ -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
}
}
\ No newline at end of file
diff --git a/package.json b/package.json
index 0320d64..ca786a7 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/actions/createModActionTicket.ts b/src/actions/createModActionTicket.ts
index 0162523..40f99b9 100644
--- a/src/actions/createModActionTicket.ts
+++ b/src/actions/createModActionTicket.ts
@@ -5,7 +5,6 @@
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,
diff --git a/src/actions/tickets/delete.ts b/src/actions/tickets/delete.ts
index 1985a08..c14cf0c 100644
--- a/src/actions/tickets/delete.ts
+++ b/src/actions/tickets/delete.ts
@@ -134,29 +134,27 @@
}
});
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);
}
}
diff --git a/src/api/index.ts b/src/api/index.ts
index c429452..5cc4a99 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -89,7 +89,6 @@
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);
diff --git a/src/commands/categorisationTest.ts b/src/commands/categorisationTest.ts
index f89a899..b052783 100644
--- a/src/commands/categorisationTest.ts
+++ b/src/commands/categorisationTest.ts
@@ -6,7 +6,7 @@
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 @@
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()
diff --git a/src/commands/mod/ban.ts b/src/commands/mod/ban.ts
index 0239951..2068b15 100644
--- a/src/commands/mod/ban.ts
+++ b/src/commands/mod/ban.ts
@@ -12,16 +12,14 @@
.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 @@
"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 @@
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 @@
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`)
diff --git a/src/commands/mod/info.ts b/src/commands/mod/info.ts
index b7f4b74..0ea93d8 100644
--- a/src/commands/mod/info.ts
+++ b/src/commands/mod/info.ts
@@ -14,7 +14,6 @@
.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 @@
} 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)
diff --git a/src/commands/mod/kick.ts b/src/commands/mod/kick.ts
index 793a630..eac7ca3 100644
--- a/src/commands/mod/kick.ts
+++ b/src/commands/mod/kick.ts
@@ -12,14 +12,12 @@
.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 @@
"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 @@
(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 @@
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`)
diff --git a/src/commands/mod/mute.ts b/src/commands/mod/mute.ts
index 5e1a18b..f98bd6a 100644
--- a/src/commands/mod/mute.ts
+++ b/src/commands/mod/mute.ts
@@ -18,8 +18,6 @@
.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 @@
}
// 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 @@
"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 @@
.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 = {
diff --git a/src/commands/mod/nick.ts b/src/commands/mod/nick.ts
index f842d76..3ff18ec 100644
--- a/src/commands/mod/nick.ts
+++ b/src/commands/mod/nick.ts
@@ -13,40 +13,43 @@
.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 @@
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 @@
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 {
diff --git a/src/commands/mod/purge.ts b/src/commands/mod/purge.ts
index fd8e6b8..af7beb3 100644
--- a/src/commands/mod/purge.ts
+++ b/src/commands/mod/purge.ts
@@ -132,7 +132,6 @@
}
let attachmentObject;
try {
- // @ts-ignore
const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger
let data = {
meta: {
@@ -210,6 +209,7 @@
}))
.setColor("Danger")
.send()
+ if (confirmation.cancelled) return
if (confirmation.success) {
let messages;
try {
@@ -234,7 +234,6 @@
}
let attachmentObject;
try {
- // @ts-ignore
const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger
let data = {
meta: {
diff --git a/src/commands/mod/slowmode.ts b/src/commands/mod/slowmode.ts
index d9a8421..2b386fd 100644
--- a/src/commands/mod/slowmode.ts
+++ b/src/commands/mod/slowmode.ts
@@ -31,6 +31,7 @@
+ `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)
diff --git a/src/commands/mod/softban.ts b/src/commands/mod/softban.ts
index a368e7a..7fefb1b 100644
--- a/src/commands/mod/softban.ts
+++ b/src/commands/mod/softban.ts
@@ -13,14 +13,12 @@
.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 @@
"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 @@
], 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`)
diff --git a/src/commands/mod/unban.ts b/src/commands/mod/unban.ts
index e512084..035b809 100644
--- a/src/commands/mod/unban.ts
+++ b/src/commands/mod/unban.ts
@@ -36,12 +36,12 @@
+ `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: {
diff --git a/src/commands/mod/unmute.ts b/src/commands/mod/unmute.ts
index d5f4205..56a0b56 100644
--- a/src/commands/mod/unmute.ts
+++ b/src/commands/mod/unmute.ts
@@ -11,14 +11,12 @@
.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 @@
"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 @@
}
}
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`)
diff --git a/src/commands/mod/warn.ts b/src/commands/mod/warn.ts
index 379d49c..3e76321 100644
--- a/src/commands/mod/warn.ts
+++ b/src/commands/mod/warn.ts
@@ -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 @@
.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 @@
"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 @@
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 @@
.setStyle("SECONDARY")
.setDisabled((interaction.options.getMember("user") as GuildMember).permissionsIn(interaction.channel as Discord.TextChannel).has("VIEW_CHANNEL") === false),
])
- ],
+ ]
})
let component;
try {
diff --git a/src/commands/nucleus/suggest.ts b/src/commands/nucleus/suggest.ts
index 4e3a1c8..0c596ff 100644
--- a/src/commands/nucleus/suggest.ts
+++ b/src/commands/nucleus/suggest.ts
@@ -12,7 +12,6 @@
.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 @@
.setColor("Danger")
.setInverted(true)
.send()
+ if (confirmation.cancelled) return
if (confirmation.success) {
await (client.channels.cache.get('955161206459600976') as Discord.TextChannel).send({
embeds: [
diff --git a/src/commands/role/user.ts b/src/commands/role/user.ts
index c2ade39..c431d39 100644
--- a/src/commands/role/user.ts
+++ b/src/commands/role/user.ts
@@ -28,10 +28,10 @@
.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
diff --git a/src/commands/settings/logs/channel.ts b/src/commands/settings/logs/channel.ts
index 10a5887..aacc2e7 100644
--- a/src/commands/settings/logs/channel.ts
+++ b/src/commands/settings/logs/channel.ts
@@ -50,6 +50,7 @@
.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})
diff --git a/src/commands/settings/logs/ignore.ts b/src/commands/settings/logs/ignore.ts
index 3b81d42..59f6621 100644
--- a/src/commands/settings/logs/ignore.ts
+++ b/src/commands/settings/logs/ignore.ts
@@ -96,6 +96,7 @@
+ `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])
diff --git a/src/commands/settings/staff.ts b/src/commands/settings/staff.ts
deleted file mode 100644
index e0d2776..0000000
--- a/src/commands/settings/staff.ts
+++ /dev/null
@@ -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 };
diff --git a/src/commands/settings/tickets.ts b/src/commands/settings/tickets.ts
index ba16751..16d5f3b 100644
--- a/src/commands/settings/tickets.ts
+++ b/src/commands/settings/tickets.ts
@@ -109,6 +109,7 @@
.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 @@
}
} 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)
diff --git a/src/commands/settings/verify.ts b/src/commands/settings/verify.ts
index 7a68c64..d71fdf0 100644
--- a/src/commands/settings/verify.ts
+++ b/src/commands/settings/verify.ts
@@ -47,6 +47,7 @@
.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});
diff --git a/src/commands/tag.ts b/src/commands/tag.ts
index d032598..0c44e37 100644
--- a/src/commands/tag.ts
+++ b/src/commands/tag.ts
@@ -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 = (interaction: CommandInteraction) => {
- interaction.reply("This command is not yet finished [tag]");
+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 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 };
\ No newline at end of file
diff --git a/src/commands/tags/create.ts b/src/commands/tags/create.ts
index 6dbecf4..0e0d54d 100644
--- a/src/commands/tags/create.ts
+++ b/src/commands/tags/create.ts
@@ -52,6 +52,7 @@
.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")
diff --git a/src/commands/tags/delete.ts b/src/commands/tags/delete.ts
index 2707465..74ebb25 100644
--- a/src/commands/tags/delete.ts
+++ b/src/commands/tags/delete.ts
@@ -32,6 +32,7 @@
.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 @@
.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")
diff --git a/src/commands/tags/edit.ts b/src/commands/tags/edit.ts
index 77e21ae..2ecdfbf 100644
--- a/src/commands/tags/edit.ts
+++ b/src/commands/tags/edit.ts
@@ -60,6 +60,7 @@
.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")
diff --git a/src/config/default.json b/src/config/default.json
index 81c7880..2f95e94 100644
--- a/src/config/default.json
+++ b/src/config/default.json
@@ -52,7 +52,7 @@
"channel": null,
"message": null
},
- "stats": [],
+ "stats": {},
"logging": {
"logs": {
"enabled": true,
diff --git a/src/config/emojis.json b/src/config/emojis.json
index 2f32df1..7024a73 100644
--- a/src/config/emojis.json
+++ b/src/config/emojis.json
@@ -22,6 +22,10 @@
"FILTER": "990242059451514902",
"ATTACHMENT": "997570687193587812",
"LOGGING": "999613304446144562",
+ "NOTIFY": {
+ "ON": "1000726394579464232",
+ "OFF": "1000726363495477368"
+ },
"OPP": {
"ADD": "837355918831124500",
"REMOVE": "837355918420869162"
diff --git a/src/events/guildBanAdd.ts b/src/events/guildBanAdd.ts
index 64a1604..d095049 100644
--- a/src/events/guildBanAdd.ts
+++ b/src/events/guildBanAdd.ts
@@ -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';
diff --git a/src/events/guildBanRemove.ts b/src/events/guildBanRemove.ts
index a6d1c14..4d6d1f8 100644
--- a/src/events/guildBanRemove.ts
+++ b/src/events/guildBanRemove.ts
@@ -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';
diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts
index 1fd1e1f..900c774 100644
--- a/src/events/interactionCreate.ts
+++ b/src/events/interactionCreate.ts
@@ -3,18 +3,43 @@
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"))) }
+ }
}
}
diff --git a/src/events/memberJoin.ts b/src/events/memberJoin.ts
index b1c2700..7bee084 100644
--- a/src/events/memberJoin.ts
+++ b/src/events/memberJoin.ts
@@ -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 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 @@
}
log(data);
} catch {}
+ try { statsChannelAdd(_, member, ); } catch {}
}
diff --git a/src/events/memberLeave.ts b/src/events/memberLeave.ts
index 592a630..122e01a 100644
--- a/src/events/memberLeave.ts
+++ b/src/events/memberLeave.ts
@@ -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 @@
}
log(data);
} catch (e) { console.log(e) }
+ try { await statsChannelRemove(client, member); } catch {}
}
diff --git a/src/events/messageCreate.ts b/src/events/messageCreate.ts
index 7db7d39..dce1959 100644
--- a/src/events/messageCreate.ts
+++ b/src/events/messageCreate.ts
@@ -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 @@
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 @@
}
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 @@
}
}
}
- });
+ };
}
if(!message) return;
diff --git a/src/events/messageDelete.ts b/src/events/messageDelete.ts
index 2263d51..3c23739 100644
--- a/src/events/messageDelete.ts
+++ b/src/events/messageDelete.ts
@@ -14,6 +14,7 @@
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 @@
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"
diff --git a/src/index.ts b/src/index.ts
index 0ae917c..5f5987e 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -11,6 +11,9 @@
client.on("ready", () => {
runServer(client);
});
+process.on("unhandledRejection", (err) => {
+ console.error(err);
+});
client.logger = new Logger()
client.verify = {}
diff --git a/src/reflex/scanners.ts b/src/reflex/scanners.ts
index 8ea22f7..a1a5974 100644
--- a/src/reflex/scanners.ts
+++ b/src/reflex/scanners.ts
@@ -4,14 +4,16 @@
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 @@
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 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 MalwareCheck(element): Promise<boolean> {
try {
- //@ts-ignore
- return (await scan.testMalware(element)).safe
+ return (await testMalware(element)).safe
} catch {
return true
}
diff --git a/src/reflex/statsChannelAdd.ts b/src/reflex/statsChannelAdd.ts
deleted file mode 100644
index 32de0ff..0000000
--- a/src/reflex/statsChannelAdd.ts
+++ /dev/null
@@ -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)
- }
- }
- });
-}
diff --git a/src/reflex/statsChannelRemove.ts b/src/reflex/statsChannelRemove.ts
deleted file mode 100644
index c6d4e65..0000000
--- a/src/reflex/statsChannelRemove.ts
+++ /dev/null
@@ -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)
- }
- }
- });
-}
\ No newline at end of file
diff --git a/src/utils/confirmationMessage.ts b/src/utils/confirmationMessage.ts
index da10cfb..988fd37 100644
--- a/src/utils/confirmationMessage.ts
+++ b/src/utils/confirmationMessage.ts
@@ -3,20 +3,24 @@
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 @@
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 @@
}
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 @@
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 };
}
}
}
diff --git a/src/utils/convertCurlyBracketString.ts b/src/utils/convertCurlyBracketString.ts
index 0277751..6ffdc8f 100644
--- a/src/utils/convertCurlyBracketString.ts
+++ b/src/utils/convertCurlyBracketString.ts
@@ -2,12 +2,12 @@
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
}
diff --git a/src/utils/database.ts b/src/utils/database.ts
index c0ae9be..5b1d6d9 100644
--- a/src/utils/database.ts
+++ b/src/utils/database.ts
@@ -63,8 +63,13 @@
}
}
- 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 @@
channel: string | null,
message: string | null,
}
- stats: {
- enabled: boolean,
- channel: string | null,
- text: string | null,
- }[]
+ stats: {}
logging: {
logs: {
enabled: boolean,