for coded
diff --git a/src/events/channelCreate.ts b/src/events/channelCreate.ts
new file mode 100644
index 0000000..f7ec756
--- /dev/null
+++ b/src/events/channelCreate.ts
@@ -0,0 +1,71 @@
+import { Interaction } from "discord.js";
+
+export const event = 'channelCreate'
+
+export async function callback(client, channel) {
+ const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderChannel } = channel.client.logger
+ let auditLog = await getAuditLog(channel.guild, 'CHANNEL_CREATE');
+ let audit = auditLog.entries.filter(entry => entry.target.id == channel.id).first();
+ if (audit.executor.id == client.user.id) return;
+ let emoji;
+ let readableType;
+ let displayName;
+ switch (channel.type) {
+ case 'GUILD_TEXT': {
+ emoji = "CHANNEL.TEXT.CREATE";
+ readableType = "Text";
+ displayName = "Text Channel"
+ break;
+ }
+ case 'GUILD_NEWS': {
+ emoji = "CHANNEL.TEXT.CREATE";
+ readableType = "Announcement";
+ displayName = "Announcement Channel"
+ break;
+ }
+ case 'GUILD_VOICE': {
+ emoji = "CHANNEL.VOICE.CREATE";
+ readableType = "Voice";
+ displayName = "Voice Channel"
+ break;
+ }
+ case 'GUILD_STAGE': {
+ emoji = "CHANNEL.VOICE.CREATE";
+ readableType = "Stage";
+ displayName = "Stage Channel"
+ }
+ case 'GUILD_CATEGORY': {
+ emoji = "CHANNEL.CATEGORY.CREATE";
+ readableType = "Category";
+ displayName = "Category"
+ break;
+ }
+ default: {
+ emoji = "CHANNEL.TEXT.CREATE";
+ readableType = "Channel";
+ displayName = "Channel"
+ }
+ }
+ let data = {
+ meta: {
+ type: 'channelCreate',
+ displayName: displayName + ' Created',
+ calculateType: 'channelUpdate',
+ color: NucleusColors.green,
+ emoji: emoji,
+ timestamp: channel.createdTimestamp
+ },
+ list: {
+ id: entry(channel.id, `\`${channel.id}\``),
+ name: entry(channel.name, renderChannel(channel)),
+ type: entry(channel.type, readableType),
+ category: entry(channel.parent ? channel.parent.id : null, channel.parent ? channel.parent.name : "Uncategorised"),
+ createdBy: entry(audit.executor.id, renderUser(audit.executor)),
+ created: entry(channel.createdTimestamp, renderDelta(channel.createdTimestamp))
+ },
+ hidden: {
+ guild: channel.guild.id
+ }
+ }
+ log(data, channel.client);
+}
diff --git a/src/events/channelDelete.ts b/src/events/channelDelete.ts
new file mode 100644
index 0000000..49bd3c3
--- /dev/null
+++ b/src/events/channelDelete.ts
@@ -0,0 +1,62 @@
+export const event = 'channelDelete'
+
+export async function callback(client, channel) {
+ const { getAuditLog, log, NucleusColors, entry, renderDelta, renderUser } = channel.client.logger
+
+ let auditLog = await getAuditLog(channel.guild, 'CHANNEL_DELETE');
+ let audit = auditLog.entries.filter(entry => entry.target.id == channel.id).first();
+ if (audit.executor.id == client.user.id) return;
+
+ let emoji;
+ let readableType;
+ let displayName;
+ switch (channel.type) {
+ case 'GUILD_TEXT': {
+ emoji = "CHANNEL.TEXT.DELETE";
+ readableType = "Text";
+ displayName = "Text Channel"
+ break;
+ }
+ case 'GUILD_VOICE': {
+ emoji = "CHANNEL.VOICE.DELETE";
+ readableType = "Voice";
+ displayName = "Voice Channel"
+ break;
+ }
+ case 'GUILD_CATEGORY': {
+ emoji = "CHANNEL.CATEGORY.DELETE";
+ readableType = "Category";
+ displayName = "Category"
+ break;
+ }
+ default: {
+ emoji = "CHANNEL.TEXT.DELETE";
+ readableType = "Channel";
+ displayName = "Channel"
+ }
+ }
+
+ let data = {
+ meta:{
+ type: 'channelDelete',
+ displayName: displayName + ' Deleted',
+ calculateType: 'channelUpdate',
+ color: NucleusColors.red,
+ emoji: emoji,
+ timestamp: audit.createdTimestamp
+ },
+ list: { // TODO: Add stuff like nsfw, theres loads missing here
+ id: entry(channel.id, `\`${channel.id}\``),
+ name: entry(channel.id, `${channel.name}`),
+ type: entry(channel.type, readableType),
+ category: entry(channel.parent ? channel.parent.id : null, channel.parent ? channel.parent.name : "Uncategorised"),
+ created: entry(channel.createdTimestamp, renderDelta(channel.createdTimestamp)),
+ deleted: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+ deletedBy: entry(audit.executor.id, renderUser(audit.executor))
+ },
+ hidden: {
+ guild: channel.guild.id
+ }
+ }
+ log(data, channel.client);
+}
diff --git a/src/events/channelUpdate.ts b/src/events/channelUpdate.ts
new file mode 100644
index 0000000..f5d2f4d
--- /dev/null
+++ b/src/events/channelUpdate.ts
@@ -0,0 +1,133 @@
+import humanizeDuration from 'humanize-duration';
+import readConfig from '../utils/readConfig.js'
+import getEmojiByName from '../utils/getEmojiByName.js';
+
+export const event = 'channelUpdate';
+
+export async function callback(client, oc, nc) {
+ let config = await readConfig(nc.guild.id);
+ const { getAuditLog, log, NucleusColors, entry, renderDelta, renderUser, renderChannel } = client.logger
+
+ if (nc.parent && (nc.parent.id == config.tickets.category)) return
+
+ let auditLog = await getAuditLog(nc.guild, 'CHANNEL_UPDATE');
+ let audit = auditLog.entries.filter(entry => entry.target.id == nc.id).first();
+ if (audit.executor.id == client.user.id) return;
+
+ let emoji:string;
+ let readableType:string;
+ let displayName:string ;
+ let changes = {
+ id: entry(nc.id, `\`${nc.id}\``),
+ channel: entry(nc.id, renderChannel(nc)),
+ edited: entry(nc.createdTimestamp, renderDelta(nc.createdTimestamp)),
+ editedBy: entry(audit.executor.id, renderUser((await nc.guild.members.fetch(audit.executor.id)).user)),
+ }
+ if (oc.name != nc.name) changes["name"] = entry([oc.name, nc.name], `${oc.name} -> ${nc.name}`);
+ if (oc.position != nc.position) changes["position"] = entry([oc.position, nc.position], `${oc.position} -> ${nc.position}`);
+
+ switch (nc.type) {
+ case 'GUILD_TEXT': {
+ emoji = "CHANNEL.TEXT.EDIT";
+ readableType = "Text";
+ displayName = "Text Channel"
+ let oldTopic = oc.topic, newTopic = nc.topic;
+ if (oldTopic) {
+ if (oldTopic.length > 256) oldTopic = `\`\`\`\n${oldTopic.replace('`', "'").substring(0, 253) + '...'}\n\`\`\``
+ else oldTopic = `\`\`\`\n${oldTopic.replace('`', "'")}\n\`\`\``
+ } else { oldTopic = "None"; }
+ if (newTopic) {
+ if (newTopic.length > 256) newTopic = `\`\`\`\n${newTopic.replace('`', "'").substring(0, 253) + '...'}\n\`\`\``
+ else newTopic = `\`\`\`\n${newTopic.replace('`', "'")}\n\`\`\``
+ } else { newTopic = "None"; }
+ let nsfw = ["", ""]
+ nsfw[0] = oc.nsfw ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`;
+ nsfw[1] = nc.nsfw ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`;
+ if (oc.topic != nc.topic) changes["description"] = entry([oc.topic, nc.topic], `\nBefore: ${oldTopic}\nAfter: ${newTopic}`);
+ if (oc.nsfw != nc.nsfw) changes["nsfw"] = entry([oc.nsfw, nc.nsfw], `${nsfw[0]} -> ${nsfw[1]}`);
+ if (oc.rateLimitPerUser != nc.rateLimitPerUser) changes["rateLimitPerUser"] = entry(
+ [oc.rateLimitPerUser, nc.rateLimitPerUser],
+ `${humanizeDuration(oc.rateLimitPerUser * 1000)} -> ${humanizeDuration(nc.rateLimitPerUser * 1000)}`
+ );
+ break;
+ }
+ case 'GUILD_NEWS': {
+ emoji = "CHANNEL.TEXT.EDIT";
+ readableType = "News";
+ displayName = "News Channel"
+ let oldTopic = oc.topic, newTopic = nc.topic;
+ if (oldTopic) {
+ if (oldTopic.length > 256) oldTopic = `\`\`\`\n${oldTopic.replace('`', "'").substring(0, 253) + '...'}\n\`\`\``
+ else oldTopic = `\`\`\`\n${oldTopic.replace('`', "'")}\n\`\`\``
+ } else { oldTopic = "None"; }
+ if (newTopic) {
+ if (newTopic.length > 256) newTopic = `\`\`\`\n${newTopic.replace('`', "'").substring(0, 253) + '...'}\n\`\`\``
+ else newTopic = `\`\`\`\n${newTopic.replace('`', "'")}\n\`\`\``
+ } else { newTopic = "None"; }
+ if (oc.nsfw != nc.nsfw) changes["nsfw"] = entry([oc.nsfw, nc.nsfw], `${oc.nsfw ? "On" : "Off"} -> ${nc.nsfw ? "On" : "Off"}`);
+ break;
+ }
+ case 'GUILD_VOICE': {
+ emoji = "CHANNEL.VOICE.EDIT";
+ readableType = "Voice";
+ displayName = "Voice Channel"
+ if (oc.bitrate != nc.bitrate) changes["bitrate"] = entry([oc.bitrate, nc.bitrate], `${oc.bitrate} -> ${nc.bitrate}`);
+ if (oc.userLimit != nc.userLimit) changes["maxUsers"] = entry([oc.userLimit, nc.userLimit], `${oc.userLimit ? oc.userLimit : "Unlimited"} -> ${nc.userLimit}`);
+ if (oc.rtcRegion != nc.rtcRegion) changes["region"] = entry(
+ [oc.rtcRegion, nc.rtcRegion],
+ `${oc.rtcRegion || "Automatic"} -> ${nc.rtcRegion || "Automatic"}`
+ );
+ break;
+ }
+ case 'GUILD_STAGE': {
+ emoji = "CHANNEL.VOICE.EDIT";
+ readableType = "Stage";
+ displayName = "Stage Channel"
+ let oldTopic = oc.topic, newTopic = nc.topic;
+ if (oldTopic) {
+ if (oldTopic.length > 256) oldTopic = `\`\`\`\n${oldTopic.replace('`', "'").substring(0, 253) + '...'}\n\`\`\``
+ else oldTopic = `\`\`\`\n${oldTopic.replace('`', "'")}\n\`\`\``
+ } else { oldTopic = "None"; }
+ if (newTopic) {
+ if (newTopic.length > 256) newTopic = `\`\`\`\n${newTopic.replace('`', "'").substring(0, 253) + '...'}\n\`\`\``
+ else newTopic = `\`\`\`\n${newTopic.replace('`', "'")}\n\`\`\``
+ } else { newTopic = "None"; }
+ if (oc.bitrate != nc.bitrate) changes["bitrate"] = entry([oc.bitrate, nc.bitrate], `${oc.bitrate} -> ${nc.bitrate}`);
+ if (oc.userLimit != nc.userLimit) changes["maxUsers"] = entry([oc.userLimit, nc.userLimit], `${oc.userLimit ? oc.userLimit : "Unlimited"} -> ${nc.userLimit}`);
+ if (oc.rtcRegion != nc.rtcRegion) changes["region"] = entry(
+ [oc.rtcRegion, nc.rtcRegion],
+ `${oc.rtcRegion || "Automatic"} -> ${nc.rtcRegion || "Automatic"}`
+ );
+ break;
+ }
+ case 'GUILD_CATEGORY': {
+ emoji = "CHANNEL.CATEGORY.EDIT";
+ readableType = "Category";
+ displayName = "Category"
+ break;
+ }
+ default: {
+ emoji = "CHANNEL.TEXT.EDIT";
+ readableType = "Channel";
+ displayName = "Channel"
+ }
+ }
+ let t = oc.type.split("_")[1];
+ if (oc.type != nc.type) changes["type"] = entry([oc.type, nc.type], `${t[0] + t.splice(1).toLowerCase()} -> ${readableType}`);
+
+ let data = {
+ meta:{
+ type: 'channelUpdate',
+ displayName: displayName + ' Edited',
+ calculateType: 'channelUpdate',
+ color: NucleusColors.yellow,
+ emoji: emoji,
+ timestamp: audit.createdTimestamp
+ },
+ list: changes,
+ hidden: {
+ guild: nc.guild.id
+ }
+ }
+ log(data, client);
+}
\ No newline at end of file
diff --git a/src/events/emojiCreate.ts b/src/events/emojiCreate.ts
new file mode 100644
index 0000000..3320267
--- /dev/null
+++ b/src/events/emojiCreate.ts
@@ -0,0 +1,28 @@
+export const event = 'emojiCreate'
+
+export async function callback(client, emoji) {
+ const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderEmoji } = emoji.client.logger
+ let auditLog = await getAuditLog(emoji.guild, 'EMOJI_CREATE');
+ let audit = auditLog.entries.filter(entry => entry.target.id == emoji.id).first();
+ if (audit.executor.id == client.user.id) return;
+ let data = {
+ meta: {
+ type: 'emojiCreate',
+ displayName: 'Emoji Created',
+ calculateType: 'emojiUpdate',
+ color: NucleusColors.green,
+ emoji: "GUILD.EMOJI.CREATE",
+ timestamp: emoji.createdTimestamp
+ },
+ list: {
+ id: entry(emoji.id, `\`${emoji.id}\``),
+ emoji: entry(emoji.name, renderEmoji(emoji)),
+ createdBy: entry(audit.executor.id, renderUser(audit.executor)),
+ created: entry(emoji.createdTimestamp, renderDelta(emoji.createdTimestamp))
+ },
+ hidden: {
+ guild: emoji.guild.id
+ }
+ }
+ log(data, client);
+}
diff --git a/src/events/emojiDelete.ts b/src/events/emojiDelete.ts
new file mode 100644
index 0000000..d31b9c6
--- /dev/null
+++ b/src/events/emojiDelete.ts
@@ -0,0 +1,29 @@
+export const event = 'emojiDelete'
+
+export async function callback(client, emoji) {
+ const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderEmoji } = emoji.client.logger
+ let auditLog = await getAuditLog(emoji.guild, 'EMOJI_DELETE');
+ let audit = auditLog.entries.filter(entry => entry.target.id == emoji.id).first();
+ if (audit.executor.id == client.user.id) return;
+ let data = {
+ meta: {
+ type: 'emojiDelete',
+ displayName: 'Emoji Deleted',
+ calculateType: 'emojiUpdate',
+ color: NucleusColors.red,
+ emoji: "GUILD.EMOJI.DELETE",
+ timestamp: audit.createdTimestamp,
+ },
+ list: {
+ id: entry(emoji.id, `\`${emoji.id}\``),
+ emoji: entry(emoji.name, renderEmoji(emoji)),
+ deletedBy: entry(audit.executor.id, renderUser(audit.executor)),
+ created: entry(emoji.createdTimestamp, renderDelta(emoji.createdTimestamp)),
+ deleted: entry(audit.createdTimestamp, renderDelta(audit.createdTimestamp)),
+ },
+ hidden: {
+ guild: emoji.guild.id
+ }
+ }
+ log(data, client);
+}
diff --git a/src/events/emojiUpdate.ts b/src/events/emojiUpdate.ts
new file mode 100644
index 0000000..2566fed
--- /dev/null
+++ b/src/events/emojiUpdate.ts
@@ -0,0 +1,35 @@
+import getEmojiByName from "../utils/getEmojiByName.js";
+
+export const event = 'emojiUpdate';
+
+export async function callback(client, oe, ne) {
+ const { getAuditLog, log, NucleusColors, entry, renderDelta, renderUser, renderEmoji } = client.logger
+
+ if (oe.name == ne.name) return
+ let auditLog = await getAuditLog(ne.guild, 'EMOJI_UPDATE');
+ let audit = auditLog.entries.first();
+ if (audit.executor.id == client.user.id) return;
+
+ let changes = {
+ id: entry(ne.id, `\`${ne.id}\``),
+ emoji: entry(ne.id, renderEmoji(ne)),
+ edited: entry(ne.createdTimestamp, renderDelta(ne.createdTimestamp)),
+ editedBy: entry(audit.executor.id, renderUser((await ne.guild.members.fetch(audit.executor.id)).user)),
+ name: entry([oe.name, ne.name], `\`:${oe.name}:\` -> \`:${ne.name}:\``),
+ }
+ let data = {
+ meta:{
+ type: 'emojiUpdate',
+ displayName: 'Emoji Edited',
+ calculateType: 'emojiUpdate',
+ color: NucleusColors.yellow,
+ emoji: "GUILD.EMOJI.EDIT",
+ timestamp: audit.createdTimestamp
+ },
+ list: changes,
+ hidden: {
+ guild: ne.guild.id
+ }
+ }
+ log(data, client);
+}
\ No newline at end of file
diff --git a/src/events/guildCreate.ts b/src/events/guildCreate.ts
new file mode 100644
index 0000000..b437b37
--- /dev/null
+++ b/src/events/guildCreate.ts
@@ -0,0 +1,56 @@
+import { MessageActionRow, MessageButton } from "discord.js";
+import generateEmojiEmbed from "../utils/generateEmojiEmbed.js";
+import getEmojiByName from "../utils/getEmojiByName";
+
+export const event = 'guildCreate';
+
+export const callback = async (client, guild) => {
+ let pages = [
+ new generateEmojiEmbed()
+ .setTitle("Welcome to Nucleus")
+ .setDescription(
+ "Thanks for adding Nucleus to your server\n\n" +
+ "On the next few pages you can find instructions on getting started, and commands you may want to set up\n\n" +
+ "If you need support, have questions or want features, you can let us know in [Clicks](https://discord.gg/bPaNnxe)"
+ )
+ .setEmoji("NUCLEUS.LOGO")
+ .setStatus("Danger"),
+ new generateEmojiEmbed()
+ ]
+ let m = await guild.systemChannel.send({embeds: [
+ new generateEmojiEmbed()
+ .setTitle("Welcome")
+ .setDescription(`One moment...`)
+ .setStatus("Danger")
+ .setEmoji("NUCLEUS.LOADING")
+ ], fetchReply: true });
+ let page = 0;
+
+ let f = async () => {
+
+ }
+
+ while (true) {
+ // edit interaction with pages[page]
+ await m.edit({
+ embeds: [pages[page].setFooter({text: `Page ${page + 1}/${pages.length}`})],
+ components: [new MessageActionRow().addComponents([
+ new MessageButton().setCustomId("left").setEmoji(getEmojiByName("CONTROL.LEFT", "id")).setDisabled(page === 0),
+ new MessageButton().setCustomId("right").setEmoji(getEmojiByName("CONTROL.RIGHT", "id")).setDisabled(page === pages.length - 1)
+ ])],
+ fetchReply: true
+ });
+ // wait for interaction
+ let interaction = await m.awaitMessageComponent({filter:f, componentType: "BUTTON", time: 60000});
+ // change page variable accordingly
+ if (interaction.component.customId == "left") {
+ if (page > 0) page--;
+ } else if (interaction.component.customId == "right") {
+ if (page < pages.length - 1) page++;
+ } else {
+ await m.delete()
+ break;
+ }
+ // break if required
+ }
+}
\ No newline at end of file
diff --git a/src/events/memberJoin.ts b/src/events/memberJoin.ts
new file mode 100644
index 0000000..317cc3a
--- /dev/null
+++ b/src/events/memberJoin.ts
@@ -0,0 +1,33 @@
+import { callback as statsChannelAdd } from '../automations/statsChannelAdd.js';
+import { callback as welcome } from '../automations/welcome.js';
+import log from '../utils/log.js';
+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
+ let data = {
+ meta: {
+ type: 'memberJoin',
+ displayName: 'Member Joined',
+ calculateType: 'guildMemberUpdate',
+ color: NucleusColors.green,
+ emoji: "MEMBER" + (member.user.bot ? ".BOT" : "") + ".JOIN",
+ timestamp: member.joinedTimestamp
+ },
+ list: {
+ id: entry(member.id, `\`${member.id}\``),
+ name: entry(member.id, renderUser(member.user)),
+ joined: entry(member.joinedAt, renderDelta(member.joinedAt)),
+ accountCreated: entry(member.user.createdAt, renderDelta(member.user.createdAt)),
+ serverMemberCount: member.guild.memberCount,
+ },
+ hidden: {
+ guild: member.guild.id
+ }
+ }
+ log(data, member.client);
+ } catch {}
+}
diff --git a/src/events/memberLeave.ts b/src/events/memberLeave.ts
new file mode 100644
index 0000000..46d7696
--- /dev/null
+++ b/src/events/memberLeave.ts
@@ -0,0 +1,34 @@
+import humanizeDuration from 'humanize-duration';
+import { callback as statsChannelRemove } from '../automations/statsChannelRemove.js';
+
+export const event = 'guildMemberRemove'
+
+export async function callback(_, member) {
+ try { await statsChannelRemove(_, member); } catch {}
+ try {
+ const { log, NucleusColors, entry, renderUser, renderDelta } = member.client.logger
+ let data = {
+ meta: {
+ type: 'memberLeave',
+ displayName: 'Member Left',
+ calculateType: 'guildMemberUpdate',
+ color: NucleusColors.red,
+ emoji: "MEMBER" + (member.user.bot ? ".BOT" : "") + ".LEAVE",
+ timestamp: new Date().getTime()
+ },
+ list: {
+ id: entry(member.id, `\`${member.id}\``),
+ name: entry(member.id, renderUser(member.user)),
+ joined: entry(member.joinedAt, renderDelta(member.joinedAt)),
+ left: entry(new Date().getTime(), renderDelta(new Date().getTime())),
+ timeInServer: entry(new Date().getTime() - member.joinedAt, humanizeDuration(new Date().getTime() - member.joinedAt, { round: true })),
+ accountCreated: entry(member.user.createdAt, renderDelta(member.user.createdAt)),
+ serverMemberCount: member.guild.memberCount,
+ },
+ hidden: {
+ guild: member.guild.id
+ }
+ }
+ log(data, member.client);
+ } catch {}
+}
diff --git a/src/events/messageChecks.ts b/src/events/messageChecks.ts
new file mode 100644
index 0000000..71ca965
--- /dev/null
+++ b/src/events/messageChecks.ts
@@ -0,0 +1,101 @@
+import { LinkCheck, MalwareCheck, NSFWCheck, SizeCheck, TestString } from '../automations/unscan.js'
+import readConfig from '../utils/readConfig.js'
+import { Message } from 'discord.js'
+
+export const event = 'messageCreate'
+
+export async function callback(_, message) {
+ if (message.author.bot) return
+ if (message.channel.type === 'dm') return
+
+ let content = message.content.toLowerCase() || ''
+ let config = await readConfig(message.guild.id);
+
+ if (config.filters.invite.enabled) {
+ if (!config.filters.invite.allowed.users.includes(message.author.id) ||
+ !config.filters.invite.allowed.channels.includes(message.channel.id) ||
+ !message.author.roles.cache.some(role => config.filters.invite.allowed.roles.includes(role.id))
+ ) {
+ if ((/(?:https?:\/\/)?discord(?:app)?\.(?:com\/invite|gg)\/[a-zA-Z0-9]+\/?/.test(content))) {
+ message.delete();
+ return toLog(message, 'invite', content.match(/(?:https?:\/\/)?discord(?:app)?\.(?:com\/invite|gg)\/[a-zA-Z0-9]+\/?/))
+ }
+ }
+ }
+
+ let attachments = message.attachments.map(element => element)
+ attachments = [...attachments, ...content.match(
+ /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi
+ ) ?? []].filter(element => (element.url ? element.url : element))
+ if (attachments.length > 0) {
+ attachments.forEach(async element => {
+ if(!message) return;
+ let url = element.url ? element.url : element
+ if (url != undefined) {
+ if(/\.+(webp|png|jpg|jpeg|bmp)/.test(url)) {
+ if (config.filters.images.NSFW && !message.channel.nsfw) {
+ if (await NSFWCheck(url)) {
+ await message.delete()
+ return toLog(message, 'NSFW', url)
+ }
+ }
+ if (config.filters.images.size) {
+ if(!url.match(/\.+(webp|png|jpg)$/gi)) return
+ if(!await SizeCheck(element)) {
+ await message.delete()
+ return toLog(message, 'size', url)
+ }
+ }
+ }
+ if (config.filters.malware) {
+ if (!MalwareCheck(url)) {
+ await message.delete()
+ return toLog(message, 'malware', url)
+ }
+ }
+ }
+ });
+ }
+ if(!message) return;
+
+ if (await LinkCheck(message)) {
+ await message.delete()
+ return toLog(message, 'link')
+ }
+
+ if (config.filters.wordFilter.enabled) {
+ let check = TestString(content, config.filters.wordFilter.words.loose, config.filters.wordFilter.words.strict)
+ if(check != "none") {
+ await message.delete()
+ return toLog(message, 'wordFilter', content)
+ }
+ }
+
+ if (!config.filters.pings.allowed.users.includes(message.author.id) ||
+ !config.filters.pings.allowed.channels.includes(message.channel.id) ||
+ !message.author.roles.cache.some(role => config.filters.pings.allowed.roles.includes(role.id))
+ ) {
+ if (config.filters.pings.everyone && message.mentions.everyone) {
+ message.delete();
+ return toLog(message, 'mention everyone')
+ }
+ if (config.filters.pings.roles) {
+ for(let role of message.mentions.roles) {
+ if(!message) return;
+ if (!config.filters.pings.allowed.roles.includes(role.id)) {
+ message.delete();
+ return toLog(message, 'mention role')
+ }
+ }
+ }
+ if(!message) return;
+ if (message.mentions.users.size >= config.filters.pings.mass && config.filters.pings.mass) {
+ message.delete();
+ return toLog(message, 'Mass Pings')
+ }
+ }
+}
+
+async function toLog(message: Message, reason: string, data?: any) {
+ // log(message.guild.id, {type: reason, data: data})
+}
diff --git a/src/events/messageDelete.ts b/src/events/messageDelete.ts
new file mode 100644
index 0000000..3b7c5c2
--- /dev/null
+++ b/src/events/messageDelete.ts
@@ -0,0 +1,38 @@
+export const event = 'messageDelete'
+
+export async function callback(client, message) {
+ if (message.author.id == client.user.id) return;
+ const { log, NucleusColors, entry, renderUser, renderDelta, renderChannel } = message.channel.client.logger
+ message.reference = message.reference || {}
+ let content = message.cleanContent
+ if (content.length > 256) content = content.substring(0, 253) + '...'
+ let data = {
+ meta: {
+ type: 'messageDelete',
+ displayName: 'Message Deleted',
+ calculateType: 'messageDelete',
+ color: NucleusColors.red,
+ emoji: 'MESSAGE.DELETE',
+ timestamp: new Date().getTime()
+ },
+ separate: {
+ start: `**Message:**\n\`\`\`${content}\`\`\``
+ },
+ list: {
+ id: entry(message.id, `\`${message.id}\``),
+ sentBy: entry(message.author.id, renderUser(message.author)),
+ sentIn: entry(message.channel.id, renderChannel(message.channel)),
+ deleted: entry(new Date(message.createdTimestamp), renderDelta(new Date(message.createdTimestamp))),
+ mentions: message.mentions.users.size,
+ attachments: message.attachments.size,
+ 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"
+ )
+ },
+ hidden: {
+ guild: message.channel.guild.id
+ }
+ }
+ log(data, client);
+}
diff --git a/src/events/messageEdit.ts b/src/events/messageEdit.ts
new file mode 100644
index 0000000..bb74f8d
--- /dev/null
+++ b/src/events/messageEdit.ts
@@ -0,0 +1,43 @@
+export const event = 'messageUpdate'
+
+export async function callback(client, oldMessage, newMessage) {
+ if (newMessage.author.id == client.user.id) return;
+ if (!newMessage.content || !oldMessage.content) return;
+ const { log, NucleusColors, entry, renderUser, renderDelta, renderNumberDelta, renderChannel } = newMessage.channel.client.logger
+ newMessage.reference = newMessage.reference || {}
+ let newContent = newMessage.cleanContent
+ let oldContent = oldMessage.cleanContent
+ if (newContent.length > 256) newContent = newContent.substring(0, 253) + '...'
+ if (oldContent.length > 256) oldContent = oldContent.substring(0, 253) + '...'
+ let data = {
+ meta: {
+ type: 'messageUpdate',
+ displayName: 'Message Edited',
+ calculateType: 'messageUpdate',
+ color: NucleusColors.yellow,
+ emoji: 'MESSAGE.EDIT',
+ timestamp: newMessage.editedTimestamp
+ },
+ separate: {
+ start: `**Before:**\n\`\`\`\n${oldContent}\n\`\`\`\n**After:**\n\`\`\`\n${newContent}\n\`\`\``,
+ end: `[[Jump to message]](${newMessage.url})`
+ },
+ list: {
+ id: entry(newMessage.id, `\`${newMessage.id}\``),
+ sentBy: entry(newMessage.author.id, renderUser(newMessage.author)),
+ sentIn: entry(newMessage.channel.id, renderChannel(newMessage.channel)),
+ sent: entry(new Date(newMessage.createdTimestamp), renderDelta(new Date(newMessage.createdTimestamp))),
+ edited: entry(new Date(newMessage.editedTimestamp), renderDelta(new Date(newMessage.editedTimestamp))),
+ mentions: renderNumberDelta(oldMessage.mentions.users.size, newMessage.mentions.users.size),
+ attachments: renderNumberDelta(oldMessage.attachments.size, newMessage.attachments.size),
+ repliedTo: entry(
+ newMessage.reference.messageId || null,
+ newMessage.reference.messageId ? `[[Jump to message]](https://discord.com/channels/${newMessage.guild.id}/${newMessage.channel.id}/${newMessage.reference.messageId})` : "None"
+ )
+ },
+ hidden: {
+ guild: newMessage.channel.guild.id
+ }
+ }
+ log(data, client);
+}
diff --git a/src/events/roleCreate.ts b/src/events/roleCreate.ts
new file mode 100644
index 0000000..487a45e
--- /dev/null
+++ b/src/events/roleCreate.ts
@@ -0,0 +1,28 @@
+export const event = 'roleCreate'
+
+export async function callback(client, role) {
+ const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderRole } = role.client.logger
+ let auditLog = await getAuditLog(role.guild, 'ROLE_CREATE');
+ let audit = auditLog.entries.filter(entry => entry.target.id == role.id).first();
+ if (audit.executor.id == client.user.id) return;
+ let data = {
+ meta: {
+ type: 'roleCreate',
+ displayName: 'Role Created',
+ calculateType: 'guildRoleUpdate',
+ color: NucleusColors.green,
+ emoji: "GUILD.ROLES.CREATE",
+ timestamp: role.createdTimestamp
+ },
+ list: {
+ id: entry(role.id, `\`${role.id}\``),
+ role: entry(role.name, renderRole(role)),
+ createdBy: entry(audit.executor.id, renderUser(audit.executor)),
+ created: entry(role.createdTimestamp, renderDelta(role.createdTimestamp))
+ },
+ hidden: {
+ guild: role.guild.id
+ }
+ }
+ log(data, client);
+}
diff --git a/src/events/roleDelete.ts b/src/events/roleDelete.ts
new file mode 100644
index 0000000..0c6d03e
--- /dev/null
+++ b/src/events/roleDelete.ts
@@ -0,0 +1,35 @@
+import getEmojiByName from "../utils/getEmojiByName.js";
+
+export const event = 'roleDelete'
+
+export async function callback(client, role) {
+ const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderRole } = role.client.logger
+ let auditLog = await getAuditLog(role.guild, 'ROLE_DELETE');
+ let audit = auditLog.entries.filter(entry => entry.target.id == role.id).first();
+ if (audit.executor.id == client.user.id) return;
+ let data = {
+ meta: {
+ type: 'roleDelete',
+ displayName: 'Role Deleted',
+ calculateType: 'guildRoleUpdate',
+ color: NucleusColors.red,
+ emoji: "GUILD.ROLES.DELETE",
+ timestamp: audit.createdTimestamp,
+ },
+ list: {
+ id: entry(role.id, `\`${role.id}\``),
+ role: entry(role.name, role.name),
+ color: entry(role.hexColor, `\`${role.hexColor}\``),
+ showInMemberList: entry(role.hoist, role.hoist ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`),
+ mentionable: entry(role.mentionable, role.mentionable ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`),
+ members: entry(role.members.size, `${role.members.size}`),
+ deletedBy: entry(audit.executor.id, renderUser(audit.executor)),
+ created: entry(role.createdTimestamp, renderDelta(role.createdTimestamp)),
+ deleted: entry(new Date().getTime(), renderDelta(new Date().getTime()))
+ },
+ hidden: {
+ guild: role.guild.id
+ }
+ }
+ log(data, client);
+}
diff --git a/src/events/roleUpdate.ts b/src/events/roleUpdate.ts
new file mode 100644
index 0000000..28afe0e
--- /dev/null
+++ b/src/events/roleUpdate.ts
@@ -0,0 +1,45 @@
+import getEmojiByName from "../utils/getEmojiByName.js";
+
+export const event = 'roleUpdate';
+
+export async function callback(client, or, nr) {
+ const { getAuditLog, log, NucleusColors, entry, renderDelta, renderUser, renderRole } = client.logger
+
+ let auditLog = await getAuditLog(nr.guild, 'ROLE_UPDATE');
+ let audit = auditLog.entries.first();
+ if (audit.executor.id == client.user.id) return;
+
+ let changes = {
+ id: entry(nr.id, `\`${nr.id}\``),
+ role: entry(nr.id, renderRole(nr)),
+ edited: entry(nr.createdTimestamp, renderDelta(nr.createdTimestamp)),
+ editedBy: entry(audit.executor.id, renderUser((await nr.guild.members.fetch(audit.executor.id)).user)),
+ }
+ let mentionable = ["", ""]
+ let hoist = ["", ""]
+ mentionable[0] = or.mentionable ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`
+ mentionable[1] = nr.mentionable ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`
+ hoist[0] = or.hoist ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`
+ hoist[1] = nr.hoist ? `${getEmojiByName("CONTROL.TICK")} Yes` : `${getEmojiByName("CONTROL.CROSS")} No`
+ if (or.name != nr.name) changes["name"] = entry([or.name, nr.name], `${or.name} -> ${nr.name}`);
+ if (or.position != nr.position) changes["position"] = entry([or.position, nr.position], `${or.position} -> ${nr.position}`);
+ if (or.hoist != nr.hoist) changes["showInMemberList"] = entry([or.hoist, nr.hoist], `${hoist[0]} -> ${hoist[1]}`);
+ if (or.mentionable != nr.mentionable) changes["mentionable"] = entry([or.mentionable, nr.mentionable], `${mentionable[0]} -> ${mentionable[1]}`);
+ if (or.hexColor != nr.hexColor) changes["color"] = entry([or.hexColor, nr.hexColor], `\`${or.hexColor}\` -> \`${nr.hexColor}\``);
+
+ let data = {
+ meta:{
+ type: 'roleUpdate',
+ displayName: 'Role Edited',
+ calculateType: 'guildRoleUpdate',
+ color: NucleusColors.yellow,
+ emoji: "GUILD.ROLES.EDIT",
+ timestamp: audit.createdTimestamp
+ },
+ list: changes,
+ hidden: {
+ guild: nr.guild.id
+ }
+ }
+ log(data, client);
+}
\ No newline at end of file