for coded
diff --git a/src/automations/statsChannelAdd.ts b/src/automations/statsChannelAdd.ts
new file mode 100644
index 0000000..42ec580
--- /dev/null
+++ b/src/automations/statsChannelAdd.ts
@@ -0,0 +1,24 @@
+import log from '../utils/log.js'
+import readConfig from '../utils/readConfig.js'
+import convertCurlyBracketString from '../utils/convertCurlyBracketString.js'
+
+export async function callback(_, member) {
+ let config = await readConfig(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 = await member.client.channels.fetch(element.channel)
+ if (channel.guild.id !== member.guild.id) return
+ if (!channel) return // TODO: Notify mods
+ try {
+ await channel.edit({ name: string })
+ } catch (err) {
+ console.error(err)
+ }
+ }
+ });
+}
\ No newline at end of file
diff --git a/src/automations/statsChannelRemove.ts b/src/automations/statsChannelRemove.ts
new file mode 100644
index 0000000..42ec580
--- /dev/null
+++ b/src/automations/statsChannelRemove.ts
@@ -0,0 +1,24 @@
+import log from '../utils/log.js'
+import readConfig from '../utils/readConfig.js'
+import convertCurlyBracketString from '../utils/convertCurlyBracketString.js'
+
+export async function callback(_, member) {
+ let config = await readConfig(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 = await member.client.channels.fetch(element.channel)
+ if (channel.guild.id !== member.guild.id) return
+ if (!channel) return // TODO: Notify mods
+ try {
+ await channel.edit({ name: string })
+ } catch (err) {
+ console.error(err)
+ }
+ }
+ });
+}
\ No newline at end of file
diff --git a/src/automations/unscan.ts b/src/automations/unscan.ts
new file mode 100644
index 0000000..1fb47f8
--- /dev/null
+++ b/src/automations/unscan.ts
@@ -0,0 +1,67 @@
+import * as scan from '../utils/scanners.js'
+
+export async function LinkCheck(message): Promise<boolean> {
+ let links = message.content.match(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/gi) ?? []
+ let detections = []
+ const promises = links.map(async element => {
+ try {
+ element = await scan.testLink(element)
+ } catch {}
+ detections.push({tags: element.tags || [], safe: element.safe})
+ });
+ await Promise.all(promises);
+ let types = [
+ "PHISHING", "DATING", "TRACKERS", "ADVERTISEMENTS", "FACEBOOK",
+ "AMP", "FACEBOOK TRACKERS", "IP GRABBERS", "PORN",
+ "GAMBLING", "MALWARE", "PIRACY", "RANSOMWARE",
+ "REDIRECTS", "SCAMS", "TORRENT", "HATE", "JUNK"
+ ]
+ let detectionsTypes = detections.map(element => {
+ let type = types.find(type => element.tags.includes(type))
+ if (type) return type
+ if (!element.safe) return "UNSAFE"
+ return undefined
+ }).filter(element => element !== undefined)
+ return detectionsTypes.length > 0
+}
+
+export async function NSFWCheck(element): Promise<boolean> {
+ try {
+ //@ts-ignore
+ let test = (await scan.testNSFW(element)).nsfw
+ return test
+ } catch {
+ return false
+ }
+}
+
+export async function SizeCheck(element): Promise<boolean> {
+ if (element.height == undefined || element.width == undefined) return true
+ if (element.height < 20 || element.width < 20) return false
+ return true
+}
+
+export async function MalwareCheck(element): Promise<boolean> {
+ try {
+ //@ts-ignore
+ return (await scan.testMalware(element)).safe
+ } catch {
+ return true
+ }
+}
+
+export function TestString(string, soft, strict): string {
+ for(let word of strict || []) {
+ if (string.toLowerCase().includes(word)) {
+ return "strict"
+ }
+ }
+ for(let word of soft) {
+ for(let word2 of string.match(/[a-z]+/gi) || []) {
+ if (word2 == word) {
+ return "loose"
+ }
+ }
+ }
+ return "none"
+}
\ No newline at end of file
diff --git a/src/automations/welcome.ts b/src/automations/welcome.ts
new file mode 100644
index 0000000..6505265
--- /dev/null
+++ b/src/automations/welcome.ts
@@ -0,0 +1,44 @@
+import log from '../utils/log.js'
+import readConfig from '../utils/readConfig.js'
+import convertCurlyBracketString from '../utils/convertCurlyBracketString.js'
+
+export async function callback(_, member) {
+ if (member.bot) return
+ let config = await readConfig(member.guild.id);
+ if (!config.welcome.enabled) return
+
+ if (!config.welcome.verificationRequired.role) {
+ if (config.welcome.welcomeRole) {
+ try {
+ await member.roles.add(config.welcome.welcomeRole)
+ } catch (err) {
+ console.error(err)
+ }
+ }
+ }
+
+
+ if (!config.welcome.verificationRequired.message && config.welcome.channel) {
+ let string = config.welcome.message
+ if (string) {
+ string = await convertCurlyBracketString(string, member.id, member.displayName, member.guild.name, member.guild.members)
+
+ if (config.welcome.channel === 'dm') {
+ try {
+ await member.send(string)
+ } catch (err) {
+ console.error(err)
+ }
+ } else {
+ let channel = await member.client.channels.fetch(config.welcome.channel)
+ if (channel.guild.id !== member.guild.id) return
+ if (!channel) return // TODO: Notify mods
+ try {
+ await channel.send(string)
+ } catch (err) {
+ console.error(err)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/commands/mod/nick.ts b/src/commands/mod/nick.ts
new file mode 100644
index 0000000..d23751c
--- /dev/null
+++ b/src/commands/mod/nick.ts
@@ -0,0 +1,25 @@
+import { CommandInteraction, GuildMember } from "discord.js";
+import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import { WrappedCheck } from "jshaiku";
+import confirmationMessage from "../../utils/confirmationMessage.js";
+import EmojiEmbed from "../../utils/generateEmojiEmbed.js";
+import keyValueList from "../../utils/generateKeyValueList.js";
+
+const command = (builder: SlashCommandSubcommandBuilder) =>
+ builder
+ .setName("nick")
+ .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").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) => {
+}
+
+const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+ return true;
+}
+
+export { command, callback, check };
\ No newline at end of file
diff --git a/src/commands/settings/menu.ts b/src/commands/settings/menu.ts
new file mode 100644
index 0000000..c7269c7
--- /dev/null
+++ b/src/commands/settings/menu.ts
@@ -0,0 +1,53 @@
+import { CommandInteraction, MessageEmbed, MessageSelectMenu } from "discord.js";
+import { SlashCommandSubcommandBuilder } from "@discordjs/builders";
+import { WrappedCheck } from "jshaiku";
+import readConfig from "../../utils/readConfig.js";
+import { toHexArray, toHexInteger, logs } from "../../utils/calculate.js"
+import { capitalize } from "../../utils/generateKeyValueList.js";
+
+const command = (builder: SlashCommandSubcommandBuilder) =>
+ builder
+ .setName("menu")
+ .setDescription("Shows a full UI of all settings")
+
+const callback = async (interaction: CommandInteraction) => {
+ return
+ let config = await readConfig(interaction.guild.id);
+
+ let currentValues = toHexArray(config.logging.log.toLog);
+
+ let toLogDropdownOptions = []
+
+ for(let i of logs) {
+ if(currentValues.includes(i)) {
+ toLogDropdownOptions.push({
+ name: capitalize(i),
+ value: i,
+ emoji: "TICK"
+ })
+ } else {
+ toLogDropdownOptions.push({
+ label: capitalize(i),
+ value: i,
+ emoji: "CROSS"
+ })
+ }
+ }
+
+ let toLogDropdown = new MessageSelectMenu()
+ .setCustomId("tolog")
+ .setMaxValues(22)
+ .addOptions()
+
+ let embed = new MessageEmbed()
+
+ interaction.reply("Command incomplete [settings/all]");
+}
+
+const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => {
+ return true;
+}
+
+export { command };
+export { callback };
+export { check };
\ No newline at end of file
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
diff --git a/src/events:TODO/channelPinsUpdate.ts b/src/events:TODO/channelPinsUpdate.ts
new file mode 100644
index 0000000..8a86b6d
--- /dev/null
+++ b/src/events:TODO/channelPinsUpdate.ts
@@ -0,0 +1,13 @@
+import log from '../utils/log.js'
+import * as JsonDiff from 'json-diff'
+
+export const name = ''
+export const once = false
+export async function execute(channel) {
+ let pins = (await channel.messages.fetchPinned()).map(m => m.id);
+ let oldPins = require(`../data/guilds/${channel.guild.id}/pins.json`);
+
+ let data = JsonDiff.diff(oldPins, pins, {full: true});
+
+ addLog(channel.guild.id, data)
+}
diff --git a/src/events:TODO/guildBanAdd.ts b/src/events:TODO/guildBanAdd.ts
new file mode 100644
index 0000000..c165805
--- /dev/null
+++ b/src/events:TODO/guildBanAdd.ts
@@ -0,0 +1,18 @@
+import log from '../utils/log.js'
+
+export const name = 'guildBanAdd'
+export const once = false
+export async function execute(ban) {
+ let logs = await ban.guild.fetchAuditLogs({'type': 'MEMBER_BAN_CREATE'});
+ let log = logs.entries.find(log => log.target.id === ban.user.id)
+
+ let data = {
+ id: ban.user.id,
+ username: ban.user.username,
+ reason: ban.reason,
+ bannedAt: log.createdTimestamp,
+ bannedBy: log.executor.id
+ }
+
+ log(ban.guild.id, data);
+}
\ No newline at end of file
diff --git a/src/events:TODO/guildBanRemove.ts b/src/events:TODO/guildBanRemove.ts
new file mode 100644
index 0000000..614f0f5
--- /dev/null
+++ b/src/events:TODO/guildBanRemove.ts
@@ -0,0 +1,18 @@
+module.exports = {
+ name:'guilidBanRemove',
+ once:false,
+ async execute(ban) {
+ let logs = await ban.guild.fetchAuditLogs({'type': 'MEMBER_BAN_REMOVE'});
+ let log = logs.entries.find(log => log.target.id === ban.user.id);
+
+ let data = {
+ id: ban.user.id,
+ username: ban.user.username,
+ reason: ban.reason,
+ unbannedAt: log.createdTimestamp,
+ unbannedBy: log.executor.id
+ }
+
+ addLog(ban.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/guildDelete.ts b/src/events:TODO/guildDelete.ts
new file mode 100644
index 0000000..107a6db
--- /dev/null
+++ b/src/events:TODO/guildDelete.ts
@@ -0,0 +1,7 @@
+module.exports = {
+ name:'guildDelete',
+ once:false,
+ async execute(guild) {
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/guildIntegrationsUpdate.ts b/src/events:TODO/guildIntegrationsUpdate.ts
new file mode 100644
index 0000000..863b67a
--- /dev/null
+++ b/src/events:TODO/guildIntegrationsUpdate.ts
@@ -0,0 +1,9 @@
+const {addLog} = require('../scripts/addLogs');
+const JsonDiff = require('json-diff');
+module.exports = {
+ name:'guildIntegrationsUpdate',
+ once:false,
+ async execute(args) {
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/guildMemberAvailable.ts b/src/events:TODO/guildMemberAvailable.ts
new file mode 100644
index 0000000..92c2c80
--- /dev/null
+++ b/src/events:TODO/guildMemberAvailable.ts
@@ -0,0 +1,8 @@
+const {addLog} = require('../scripts/addLogs');
+module.exports = {
+ name:'guildMemberAvailable',
+ once:false,
+ async execute(args) {
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/guildMemberUpdate.ts b/src/events:TODO/guildMemberUpdate.ts
new file mode 100644
index 0000000..238bf3c
--- /dev/null
+++ b/src/events:TODO/guildMemberUpdate.ts
@@ -0,0 +1,31 @@
+const {addLog} = require('../scripts/addLogs');
+const JsonDiff = require('json-diff');
+
+module.exports = {
+ name:'guildMemberUpdate',
+ once:false,
+ async execute(oldMember, newMember) {
+
+ let oMem = {
+ id: oldMember.id,
+ username: oldMember.user.username,
+ nick: oldMember.nickname,
+ roles: oldMember.roles.cache.map(r => r.id),
+ displayAvatarUrl: oldMember.displayAvatarUrl,
+ communicationDisabledUntil: oldMember.communicationDisabledUntilTimestamp
+ }
+
+ let nMem = {
+ id: newMember.id,
+ username: newMember.user.username,
+ nick: newMember.nickname,
+ roles: newMember.roles.cache.map(r => r.id),
+ displayAvatarUrl: newMember.displayAvatarUrl,
+ communicationDisabledUntil: newMember.communicationDisabledUntilTimestamp
+ }
+
+ let data = JsonDiff.diff(oMem, nMem, {full: true});
+
+ addLog(newMember.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/guildScheduledEventCreate.ts b/src/events:TODO/guildScheduledEventCreate.ts
new file mode 100644
index 0000000..489dd2f
--- /dev/null
+++ b/src/events:TODO/guildScheduledEventCreate.ts
@@ -0,0 +1,24 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'guildScheduledEventCreate',
+ once:false,
+ async execute(event) {
+ let data = {
+ id: event.id,
+ name: event.name,
+ description: event.description,
+ channel: event.channel.id,
+ time: {
+ start: event.scheduledStartTimestamp,
+ end: event.scheduledEndTimestamp
+ },
+ date: event.date,
+ createdBy: event.creator.id,
+ createdAt: event.createdTimestamp,
+ privacyLevel: event.privacyLevel
+ }
+
+ addLog(event.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/guildScheduledEventDelete.ts b/src/events:TODO/guildScheduledEventDelete.ts
new file mode 100644
index 0000000..a3e85db
--- /dev/null
+++ b/src/events:TODO/guildScheduledEventDelete.ts
@@ -0,0 +1,20 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'guildScheduledEventDelete',
+ once:false,
+ async execute(event) {
+
+ let logs = await event.guild.fetchAuditLogs({'type': 'GUILD_SCHEDULED_EVENT_DELETE'});
+ let log = logs.entries.find(log => log.target.id === event.id);
+
+ let data = {
+ id: event.id,
+ name: event.name,
+ deletedAt: log.createdTimestamp,
+ deletedBy: log.executor.id
+ }
+
+ addLog(event.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/guildScheduledEventUpdate.ts b/src/events:TODO/guildScheduledEventUpdate.ts
new file mode 100644
index 0000000..cc37a73
--- /dev/null
+++ b/src/events:TODO/guildScheduledEventUpdate.ts
@@ -0,0 +1,43 @@
+const {addLog} = require('../scripts/addLogs');
+const JsonDiff = require('json-diff');
+
+module.exports = {
+ name:'guildScheduledEventUpdate',
+ once:false,
+ async execute(oldEvent, newEvent) {
+ let oe = {
+ id: oldEvent.id,
+ name: oldEvent.name,
+ description: oldEvent.description,
+ channel: oldEvent.channel ? oldEvent.channel.id : null,
+ time: {
+ start: oldEvent.scheduledStartTimestamp,
+ end: oldEvent.scheduledEndTimestamp
+ },
+ date: oldEvent.date,
+ privacyLevel: oldEvent.privacyLevel,
+ entityType: oldEvent.entityType,
+ entityMetadata: oldEvent.entityMetadata,
+ status: oldEvent.status
+ }
+ let ne = {
+ id: newEvent.id,
+ name: newEvent.name,
+ description: newEvent.description,
+ channel: newEvent.channel ? newEvent.channel.id : null,
+ time: {
+ start: newEvent.scheduledStartTimestamp,
+ end: newEvent.scheduledEndTimestamp
+ },
+ date: newEvent.date,
+ privacyLevel: newEvent.privacyLevel,
+ entityType: newEvent.entityType,
+ entityMetadata: newEvent.entityMetadata,
+ status: newEvent.status
+ }
+
+ let data = JsonDiff.diff(oe, ne, {full: true});
+
+ addLog(newEvent.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/guildScheduledEventUserAdd.ts b/src/events:TODO/guildScheduledEventUserAdd.ts
new file mode 100644
index 0000000..caadd25
--- /dev/null
+++ b/src/events:TODO/guildScheduledEventUserAdd.ts
@@ -0,0 +1,20 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'guildScheduledEventUserAdd',
+ once:false,
+ async execute(event, member) {
+ let data = {
+ event: {
+ id: event.id,
+ name: event.name,
+ },
+ user: {
+ id: member.id,
+ username: member.username,
+ joinedEventAt: Date.now()
+ }
+ }
+ addLog(event.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/guildScheduledEventUserRemove.ts b/src/events:TODO/guildScheduledEventUserRemove.ts
new file mode 100644
index 0000000..9977709
--- /dev/null
+++ b/src/events:TODO/guildScheduledEventUserRemove.ts
@@ -0,0 +1,20 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'guildScheduledEventUserRemove',
+ once:false,
+ async execute(event, member) {
+ let data = {
+ event: {
+ id: event.id,
+ name: event.name,
+ },
+ user: {
+ id: member.id,
+ username: member.username,
+ leftEventAt: Date.now()
+ }
+ }
+ addLog(event.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/guildUpdate.ts b/src/events:TODO/guildUpdate.ts
new file mode 100644
index 0000000..2e93f5b
--- /dev/null
+++ b/src/events:TODO/guildUpdate.ts
@@ -0,0 +1,83 @@
+const {addLog} = require('../scripts/addLogs');
+const JsonDiff = require('json-diff');
+
+module.exports = {
+ name:'guildUpdate',
+ once:false,
+ async execute(oldGuild, newGuild) {
+
+ if(!newGuild.available) {
+ return addLog(newGuild.id, {id:newGuild.id,unavailable:true});
+ }
+
+ let og = {
+ afkChannel: oldGuild.afkChannel ? oldGuild.afkChannel.id : null,
+ afkTimeout: oldGuild.afkTimeout,
+ available: oldGuild.available,
+ banner: oldGuild.banner,
+ description: oldGuild.description,
+ discoverySplash: oldGuild.discoverySplash,
+ explicitContentFilter: oldGuild.explicitContentFilter,
+ features: oldGuild.features,
+ icon: oldGuild.icon,
+ id: oldGuild.id,
+ large: oldGuild.large,
+ maximumBitrate: oldGuild.maximumBitrate,
+ maximumMembers: oldGuild.maximumMembers,
+ mfaLevel: oldGuild.mfaLevel,
+ name: oldGuild.name,
+ nsfwLevel: oldGuild.nsfwLevel,
+ ownerid: oldGuild.ownerId,
+ partnered: oldGuild.partnered,
+ preferredLocale: oldGuild.preferredLocale,
+ premiumProgressBarEnabled: oldGuild.premiumProgressBarEnabled,
+ premiumSubscriptionCount: oldGuild.premiumSubscriptionCount,
+ premiumTier: oldGuild.premiumTier,
+ publicUpdatesChannel: oldGuild.publicUpdatesChannel ? oldGuild.publicUpdatesChannel.id : null,
+ rulesChannel: oldGuild.rulesChannel ? oldGuild.rulesChannel.id : null,
+ splash: oldGuild.splash,
+ systemChannel: oldGuild.systemChannel ? oldGuild.systemChannel.id : null,
+ vanityURLCode: oldGuild.vanityURLCode,
+ verificationLevel: oldGuild.verificationLevel,
+ verified: oldGuild.verified,
+ widgetChannel: oldGuild.widgetChannel ? oldGuild.widgetChannel.id : null,
+ widgetEnabled: oldGuild.widgetEnabled,
+ }
+ let ng = {
+ afkChannel: newGuild.afkChannel ? newGuild.afkChannel.id : null,
+ afkTimeout: newGuild.afkTimeout,
+ available: newGuild.available,
+ banner: newGuild.banner,
+ description: newGuild.description,
+ discoverySplash: newGuild.discoverySplash,
+ explicitContentFilter: newGuild.explicitContentFilter,
+ features: newGuild.features,
+ icon: newGuild.icon,
+ id: newGuild.id,
+ large: newGuild.large,
+ maximumBitrate: newGuild.maximumBitrate,
+ maximumMembers: newGuild.maximumMembers,
+ mfaLevel: newGuild.mfaLevel,
+ name: newGuild.name,
+ nsfwLevel: newGuild.nsfwLevel,
+ ownerid: newGuild.ownerId,
+ partnered: newGuild.partnered,
+ preferredLocale: newGuild.preferredLocale,
+ premiumProgressBarEnabled: newGuild.premiumProgressBarEnabled,
+ premiumSubscriptionCount: newGuild.premiumSubscriptionCount,
+ premiumTier: newGuild.premiumTier,
+ publicUpdatesChannel: newGuild.publicUpdatesChannel ? newGuild.publicUpdatesChannel.id : null,
+ rulesChannel: newGuild.rulesChannel ? newGuild.rulesChannel.id : null,
+ splash: newGuild.splash,
+ systemChannel: newGuild.systemChannel ? newGuild.systemChannel.id : null,
+ vanityURLCode: newGuild.vanityURLCode,
+ verificationLevel: newGuild.verificationLevel,
+ verified: newGuild.verified,
+ widgetChannel: newGuild.widgetChannel ? newGuild.widgetChannel.id : null,
+ widgetEnabled: newGuild.widgetEnabled,
+ }
+
+ let data = JsonDiff.diff(og, ng, {full:true});
+ addLog(newGuild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/inviteCreate.ts b/src/events:TODO/inviteCreate.ts
new file mode 100644
index 0000000..596bf58
--- /dev/null
+++ b/src/events:TODO/inviteCreate.ts
@@ -0,0 +1,22 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'inviteCreate',
+ once:false,
+ async execute(invite) {
+
+ const i = await invite.guild.invites.fetch(invite.code)
+
+ let data = {
+ channel: invite.channel.id,
+ code: invite.code,
+ createdAt: invite.createdTimestamp,
+ expiresAt: invite.expiresTimestamp,
+ createdBy: invite.inviter.id,
+ maxUsage: i.maxUses,
+ maxAge: i.maxAge
+ }
+
+ addLog(invite.guild.id, data)
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/inviteDelete.ts b/src/events:TODO/inviteDelete.ts
new file mode 100644
index 0000000..609ce6f
--- /dev/null
+++ b/src/events:TODO/inviteDelete.ts
@@ -0,0 +1,21 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'inviteDelete',
+ once:false,
+ async execute(invite) {
+
+ let logs = await invite.guild.fetchAuditLogs({type: 'INVITE_DELETE'});
+ let entry = logs.entries.find(e => e.target.code === invite.code);
+
+ let data = {
+ channel: invite.channel.id,
+ code: invite.code,
+ deletedAt: invite.deletedTimestamp,
+ deletedBy: entry.executor.id
+ }
+
+ addLog(invite.guild.id, data)
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/messageCreate.ts b/src/events:TODO/messageCreate.ts
new file mode 100644
index 0000000..46e8795
--- /dev/null
+++ b/src/events:TODO/messageCreate.ts
@@ -0,0 +1,32 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'messageCreate',
+ once:false,
+ async execute(message) {
+
+ const guildConfig = require(`../data/guilds/${message.guild.id}/config.json`);
+ if(guildConfig.images.enabled) {
+
+ }
+ if(guildConfig.wordFilter.enabled) {
+ for(word of guildConfig.wordFilter.words.strict) {
+ if(message.content.toLowerCase().includes(word)) {
+ message.delete();
+ // message.channel.send(`${message.author} has been warned for using a banned word.`);
+ break;
+ }
+ }
+ for(word of message.content.split(' ')) {
+ if(guildConfig.wordFilter.words.soft.includes(word)) {
+ message.delete();
+ // message.channel.send(`${message.author} has been warned for using a banned word.`);
+ break;
+ }
+ }
+
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/messageDeleteBulk.ts b/src/events:TODO/messageDeleteBulk.ts
new file mode 100644
index 0000000..92d9134
--- /dev/null
+++ b/src/events:TODO/messageDeleteBulk.ts
@@ -0,0 +1,25 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'messageDeleteBulk',
+ once:false,
+ async execute(messages) {
+
+ let logs = await messages.first().guild.fetchAuditLogs({type: 'MESSAGE_DELETE_BULK'});
+ let entry = logs.entries.first();
+
+ let data = {
+ messages:messages.map(message=>{
+ return {
+ id:message.id,
+ channel:message.channel.id,
+ content:message.content
+ }
+ }),
+ deletedBy:entry.executor.id,
+ deletedAt:entry.createdAt
+ }
+
+ addLog(messages.first().guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/messageReactionAdd.ts b/src/events:TODO/messageReactionAdd.ts
new file mode 100644
index 0000000..c59a16f
--- /dev/null
+++ b/src/events:TODO/messageReactionAdd.ts
@@ -0,0 +1,24 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'messageReactionAdd',
+ once:false,
+ async execute(messageReaction, user) {
+ let data = {
+ messageReaction: {
+ messageID: messageReaction.message.id,
+ reactionEmoji: {
+ name: messageReaction.emoji.name,
+ id: messageReaction.emoji.id
+ },
+ addedAt: Date.now()
+ },
+ user: {
+ id: user.id,
+ username: user.username
+ }
+ }
+
+ addLog(messageReaction.message.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/messageReactionRemove.ts b/src/events:TODO/messageReactionRemove.ts
new file mode 100644
index 0000000..685ad37
--- /dev/null
+++ b/src/events:TODO/messageReactionRemove.ts
@@ -0,0 +1,25 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'messageReactionRemove',
+ once:false,
+ async execute(messageReaction, user) {
+ let data = {
+ messageReaction: {
+ messageID: messageReaction.message.id,
+ reactionEmoji: {
+ name: messageReaction.emoji.name,
+ id: messageReaction.emoji.id
+ },
+ removedAt: Date.now()
+ },
+ user: {
+ id: user.id,
+ username: user.username
+ }
+ }
+
+ addLog(messageReaction.message.guild.id, data);
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/messageReactionRemoveAll.ts b/src/events:TODO/messageReactionRemoveAll.ts
new file mode 100644
index 0000000..e4254da
--- /dev/null
+++ b/src/events:TODO/messageReactionRemoveAll.ts
@@ -0,0 +1,20 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'messageReactionRemoveAll',
+ once:false,
+ async execute(message, reactions) {
+ let data = {
+ messageID: message.id,
+ reactions: reactions.map(r => {
+ return {
+ name: r.emoji.name,
+ id: r.emoji.id
+ }
+ }),
+ removedAt: Date.now()
+ }
+
+ addLog(message.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/messageReactionRemoveEmoji.ts b/src/events:TODO/messageReactionRemoveEmoji.ts
new file mode 100644
index 0000000..9a709f8
--- /dev/null
+++ b/src/events:TODO/messageReactionRemoveEmoji.ts
@@ -0,0 +1,15 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'messageReactionRemoveEmoji',
+ once:false,
+ async execute(messageReaction) {
+ let data = {
+ name: messageReaction.emoji.name,
+ id: messageReaction.emoji.id,
+ removedAt: Date.now()
+ }
+
+ addLog(messageReaction.message.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/stageInstanceCreate.ts b/src/events:TODO/stageInstanceCreate.ts
new file mode 100644
index 0000000..1f56418
--- /dev/null
+++ b/src/events:TODO/stageInstanceCreate.ts
@@ -0,0 +1,20 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'stageInstanceCreate',
+ once:false,
+ async execute(stageInstance) {
+ let data = {
+ id: stageInstance.id,
+ channel: stageInstance.channel.id,
+ channelName: stageInstance.channel.name,
+ createdAt: stageInstance.createdTimestamp,
+ topic: stageInstance.topic,
+ discoverable: !stageInstance.discoverableDisabled,
+ privacy: stageInstance.privacyLevel
+ }
+
+ addLog(role.guild.id, data);
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/stageInstanceDelete.ts b/src/events:TODO/stageInstanceDelete.ts
new file mode 100644
index 0000000..e8e0e78
--- /dev/null
+++ b/src/events:TODO/stageInstanceDelete.ts
@@ -0,0 +1,25 @@
+const {addLog} = require('../scripts/addLogs');
+
+module.exports = {
+ name:'stageInstanceDelete',
+ once:false,
+ async execute(stageInstance) {
+
+ let logs = await stageInstance.guild.fetchAuditLogs({type: 'STAGE_INSTANCE_DELETE'});
+ let entry = logs.entries.find(e => e.target.id === stageInstance.id);
+
+ let data = {
+ id: stageInstance.id,
+ channel: stageInstance.channel.id,
+ channelName: stageInstance.channel.name,
+ deletedAt: entry.createdTimestamp,
+ deletedBy: entry.deletedBy,
+ topic: stageInstance.topic,
+ discoverable: !stageInstance.discoverableDisabled,
+ privacy: stageInstance.privacyLevel
+ }
+
+ addLog(role.guild.id, data);
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/stageInstanceUpdate.ts b/src/events:TODO/stageInstanceUpdate.ts
new file mode 100644
index 0000000..e516915
--- /dev/null
+++ b/src/events:TODO/stageInstanceUpdate.ts
@@ -0,0 +1,30 @@
+const {addLog} = require('../scripts/addLogs');
+const JsonDiff = require('json-diff');
+
+module.exports = {
+ name:'stageInstanceUpdate',
+ once:false,
+ async execute(oldStage, newStage) {
+ let os = {
+ id: oldStage.id,
+ channel: oldStage.channel.id,
+ channelName: oldStage.channel.name,
+ topic: oldStage.topic,
+ discoverable: !oldStage.discoverableDisabled,
+ privacy: oldStage.privacyLevel
+ }
+
+ let ns = {
+ id: newStage.id,
+ channel: newStage.channel.id,
+ channelName: newStage.channel.name,
+ topic: newStage.topic,
+ discoverable: !newStage.discoverableDisabled,
+ privacy: newStage.privacyLevel
+ }
+
+ let data = JsonDiff.diff(os, ns, {full: true});
+
+ addLog(role.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/stickerCreate.ts b/src/events:TODO/stickerCreate.ts
new file mode 100644
index 0000000..c968c2a
--- /dev/null
+++ b/src/events:TODO/stickerCreate.ts
@@ -0,0 +1,18 @@
+const {addLog} = require('../scripts/addLogs');
+module.exports = {
+ name:'stickerCreate',
+ once:false,
+ async execute(sticker) {
+ let data = {
+ id: sticker.id,
+ createdAt: sticker.createdTimestamp,
+ description: sticker.description,
+ name: sticker.name,
+ type: sticker.type,
+ tags: sticker.tags,
+ createdBy: sticker.user.id
+ }
+
+ addLog(role.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/stickerDelete.ts b/src/events:TODO/stickerDelete.ts
new file mode 100644
index 0000000..67654f3
--- /dev/null
+++ b/src/events:TODO/stickerDelete.ts
@@ -0,0 +1,23 @@
+const {addLog} = require('../scripts/addLogs');
+module.exports = {
+ name:'stickerDelete',
+ once:false,
+ async execute(sticker) {
+
+ let logs = await sticker.guild.fetchAuditLogs({type: 'STICKER_DELETE'});
+ let entry = logs.entries.find(e => e.target.id === sticker.id);
+
+ let data = {
+ id: sticker.id,
+ deletedAt: entry.createdTimestamp,
+ description: sticker.description,
+ name: sticker.name,
+ type: sticker.type,
+ tags: sticker.tags,
+ deletedBy: entry.executor.id
+ }
+
+ addLog(role.guild.id, data);
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/stickerUpdate.ts b/src/events:TODO/stickerUpdate.ts
new file mode 100644
index 0000000..33e4cdf
--- /dev/null
+++ b/src/events:TODO/stickerUpdate.ts
@@ -0,0 +1,27 @@
+const {addLog} = require('../scripts/addLogs');
+const JsonDiff = require('json-diff');
+module.exports = {
+ name:'stickerUpdate',
+ once:false,
+ async execute(oldSticker, newSticker) {
+ let os = {
+ id: sticker.id,
+ description: sticker.description,
+ name: sticker.name,
+ type: sticker.type,
+ tags: sticker.tags,
+ }
+
+ let ns = {
+ id: sticker.id,
+ description: sticker.description,
+ name: sticker.name,
+ type: sticker.type,
+ tags: sticker.tags,
+ }
+
+ let data = JsonDiff.diff(os, ns, {full: true});
+
+ addLog(role.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/threadCreate.ts b/src/events:TODO/threadCreate.ts
new file mode 100644
index 0000000..63c29a4
--- /dev/null
+++ b/src/events:TODO/threadCreate.ts
@@ -0,0 +1,22 @@
+const {addLog} = require('../scripts/addLogs');
+module.exports = {
+ name:'threadCreate',
+ once:false,
+ async execute(thread) {
+
+ let data = {
+ autoArchiveDuration: thread.autoArchiveDuration,
+ id: thread.id,
+ locked: thread.locked,
+ name: thread.name,
+ parentChannel: thread.parent.id,
+ slowmode: thread.rateLimitPerUser,
+ type: thread.type,
+ createdAt: thread.createdTimestamp,
+ createdBy: thread.ownerId
+ }
+
+ addLog(thread.guild.id, data);
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/threadDelete.ts b/src/events:TODO/threadDelete.ts
new file mode 100644
index 0000000..43cba66
--- /dev/null
+++ b/src/events:TODO/threadDelete.ts
@@ -0,0 +1,25 @@
+const {addLog} = require('../scripts/addLogs');
+module.exports = {
+ name:'threadDelete',
+ once:false,
+ async execute(thread) {
+
+ let logs = await thread.guild.fetchAuditLogs({type: 'THREAD_DELETE'});
+ let entry = logs.entries.find(e => e.target.id === thread.id);
+
+ let data = {
+ autoArchiveDuration: thread.autoArchiveDuration,
+ id: thread.id,
+ locked: thread.locked,
+ name: thread.name,
+ parentChannel: thread.parent.id,
+ slowmode: thread.rateLimitPerUser,
+ type: thread.type,
+ deletedAt: entry.createdTimestamp,
+ deletedBy: entry.executor.id
+ }
+
+ addLog(thread.guild.id, data);
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/threadMembersUpdate.ts b/src/events:TODO/threadMembersUpdate.ts
new file mode 100644
index 0000000..fab4ef5
--- /dev/null
+++ b/src/events:TODO/threadMembersUpdate.ts
@@ -0,0 +1,15 @@
+const {addLog} = require('../scripts/addLogs');
+const JsonDiff = require('json-diff');
+module.exports = {
+ name:'threadMembersUpdate',
+ once:false,
+ async execute(oldMembers, newMembers) {
+
+ let om = oldMembers.map(m => m.id);
+ let nm = newMembers.map(m => m.id);
+ let data = JsonDiff.diff(om, nm);
+
+ addLog(newMembers.first().guild.id, data);
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/threadUpdate.ts b/src/events:TODO/threadUpdate.ts
new file mode 100644
index 0000000..cfaed14
--- /dev/null
+++ b/src/events:TODO/threadUpdate.ts
@@ -0,0 +1,31 @@
+const {addLog} = require('../scripts/addLogs');
+const JsonDiff = require('json-diff');
+module.exports = {
+ name:'threadUpdate',
+ once:false,
+ async execute(oldThread, newThread) {
+ let ot = {
+ autoArchiveDuration: oldThread.autoArchiveDuration,
+ id: oldThread.id,
+ locked: oldThread.locked,
+ name: oldThread.name,
+ parentChannel: oldThread.parent.id,
+ slowmode: oldThread.rateLimitPerUser,
+ type: oldThread.type,
+ }
+ let nt = {
+ autoArchiveDuration: newThread.autoArchiveDuration,
+ id: newThread.id,
+ locked: newThread.locked,
+ name: newThread.name,
+ parentChannel: newThread.parent.id,
+ slowmode: newThread.rateLimitPerUser,
+ type: newThread.type,
+ }
+
+ let data = JsonDiff.diff(ot, nt, {full: true});
+
+ addLog(newThread.guild.id, data);
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/userUpdate.ts b/src/events:TODO/userUpdate.ts
new file mode 100644
index 0000000..a6735c8
--- /dev/null
+++ b/src/events:TODO/userUpdate.ts
@@ -0,0 +1,9 @@
+const {addLog} = require('../scripts/addLogs');
+const JsonDiff = require('json-diff');
+module.exports = {
+ name:'userUpdate',
+ once:false,
+ async execute(args) {
+
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/voiceStateUpdate.ts b/src/events:TODO/voiceStateUpdate.ts
new file mode 100644
index 0000000..3754f0c
--- /dev/null
+++ b/src/events:TODO/voiceStateUpdate.ts
@@ -0,0 +1,35 @@
+const {addLog} = require('../scripts/addLogs');
+const JsonDiff = require('json-diff');
+module.exports = {
+ name:'voiceStateUpdate',
+ once:false,
+ async execute(oldState, newState) {
+ let os = {
+ channel:oldState.channel ? oldState.channel.id : null,
+ serverDeaf:oldState.serverDeaf,
+ serverMute:oldState.serverMute,
+ selfDeaf:oldState.selfDeaf,
+ selfMute:oldState.selfMute,
+ selfVideo:oldState.selfVideo,
+ streaming:oldState.streaming,
+ id:oldState.id,
+ requestToSpeakTimestamp:oldState.requestToSpeakTimestamp
+ }
+
+ let ns = {
+ channel:newState.channel ? newState.channel.id : null,
+ serverDeaf:newState.serverDeaf,
+ serverMute:newState.serverMute,
+ selfDeaf:newState.selfDeaf,
+ selfMute:newState.selfMute,
+ selfVideo:newState.selfVideo,
+ streaming:newState.streaming,
+ id:newState.id,
+ requestToSpeakTimestamp:newState.requestToSpeakTimestamp
+ }
+
+ let data = JsonDiff.diff(os, ns, {full: true});
+
+ addLog(oldState.guild.id, data);
+ }
+}
\ No newline at end of file
diff --git a/src/events:TODO/webhookUpdate.ts b/src/events:TODO/webhookUpdate.ts
new file mode 100644
index 0000000..56affc4
--- /dev/null
+++ b/src/events:TODO/webhookUpdate.ts
@@ -0,0 +1,9 @@
+const {addLog} = require('../scripts/addLogs');
+const JsonDiff = require('json-diff');
+module.exports = {
+ name:'webhookUpdate',
+ once:false,
+ async execute(args) {
+
+ }
+}
\ No newline at end of file
diff --git a/src/utils/calculate.ts b/src/utils/calculate.ts
new file mode 100644
index 0000000..174c401
--- /dev/null
+++ b/src/utils/calculate.ts
@@ -0,0 +1,66 @@
+const logs = [
+ "channelUpdate",
+ "channelPinsUpdate",
+ "emojiUpdate",
+ "stickerUpdate",
+ "guildUpdate",
+ "guildMemberUpdate",
+ "guildMemberPunish",
+ "guildEventUpdate",
+ "guildEventMemberUpdate",
+ "guildRoleUpdate",
+ "guildInviteUpdate",
+ "messageUpdate",
+ "messageDelete",
+ "messageDeleteBulk",
+ "messageReactionUpdate",
+ "messagePing",
+ "messageMassPing",
+ "messageAnnounce",
+ "stageUpdate",
+ "threadUpdate",
+ "voiceStateUpdate",
+ "webhookUpdate"
+]
+
+const tickets = [
+ "support",
+ "report",
+ "question",
+ "issue",
+ "suggestion",
+ "other"
+]
+
+const toHexInteger = (permissions, array?) => {
+ if (!array) {
+ array = logs;
+ }
+ let int = 0n;
+
+ for(let perm of permissions) {
+ int += BigInt(2 ** array.indexOf(perm));
+ }
+ return int.toString(16)
+}
+
+const toHexArray = (permissionsHex, array?) => {
+ if (!array) {
+ array = logs;
+ }
+ let permissions = [];
+ let int = (BigInt("0x" + permissionsHex)).toString(2).split('').reverse();
+ for (let index in int) {
+ if (int[index] == "1") {
+ permissions.push(array[index]);
+ }
+ }
+ return permissions;
+}
+
+export {
+ toHexInteger,
+ toHexArray,
+ tickets,
+ logs
+}
\ No newline at end of file
diff --git a/src/utils/convertCurlyBracketString.ts b/src/utils/convertCurlyBracketString.ts
new file mode 100644
index 0000000..093e522
--- /dev/null
+++ b/src/utils/convertCurlyBracketString.ts
@@ -0,0 +1,15 @@
+async function convertCurlyBracketString(str, memberID, memberName, serverName, members): Promise<string> {
+ 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}`);
+
+ return str
+}
+
+export default convertCurlyBracketString;
\ No newline at end of file
diff --git a/src/utils/generateConfig.ts b/src/utils/generateConfig.ts
new file mode 100644
index 0000000..6dc4e52
--- /dev/null
+++ b/src/utils/generateConfig.ts
@@ -0,0 +1,92 @@
+import * as fs from 'fs';
+
+function writeLogConfig(guild, logs) {
+ if( !fs.existsSync(`./data/guilds/${guild.id}/config.json`) ) {
+ fs.rmSync(`./data/guilds/${guild.id}/config.json`);
+ }
+ if( !fs.existsSync(`./data/guilds/${guild.id}/pins.json`) ) {
+ let pins = guild.channels.cache.filter(c => c.type === "GUILD_TEXT").map(
+ c => c.messages.fetchPinned().then(m => m.map(m => m.id))
+ );
+ fs.writeFileSync(`./data/guilds/${guild.id}/pins.json`, JSON.stringify(pins));
+ }
+ if( !fs.existsSync(`./data/guilds/${guild.id}/logs.json`) ) {
+ fs.writeFileSync(`./data/guilds/${guild.id}/logs.json`, JSON.stringify([]));
+ } else if( logs ) {
+ fs.rmSync(`./data/guilds/${guild.id}/logs.json`);
+ fs.writeFileSync(`./data/guilds/${guild.id}/logs.json`, JSON.stringify([]));
+ }
+ fs.writeFileSync(`./data/guilds/${guild.id}/config.json`, JSON.stringify({
+ metadata: {
+ premium: false
+ },
+ logs: {
+ enabled: true,
+ logChannel: guild.systemChannelId,
+ toLog: "8be71",
+ toIgnore: {
+ bots: false,
+ channels: [],
+ members: [],
+ roles: []
+ }
+ },
+ userVerification: {
+ enabled: false,
+ roleID: null,
+ customMessage: null
+ },
+ modmail: {
+ enabled: false,
+ categoryId: null,
+ namingScheme: "rsm-{user}-{discriminator}",
+ },
+ welcome: {
+ enabled: false,
+ channelId: null,
+ message: null,
+ messageType: "embed",
+ },
+ filters: {
+ images: {
+ NSFW: true,
+ size: true
+ },
+ malware: true,
+ wordFilter: {
+ enabled: true,
+ words: {
+ strict: [],
+ loose: []
+ },
+ allowed: {
+ users: [],
+ roles: [],
+ channels: []
+ }
+ },
+ invite: {
+ enabled: true,
+ allowed: {
+ users: [],
+ channels: [],
+ roles: []
+ }
+ },
+ pings: {
+ mass: 5,
+ everyone: true,
+ roles: true,
+ allowed: {
+ roles: [],
+ rolesToMention: [],
+ users: [],
+ channels: []
+ }
+ }
+ },
+ tags: {}
+ }));
+}
+
+export default writeLogConfig;
\ No newline at end of file
diff --git a/src/utils/log.ts b/src/utils/log.ts
new file mode 100644
index 0000000..d807831
--- /dev/null
+++ b/src/utils/log.ts
@@ -0,0 +1,101 @@
+import * as fs from 'fs';
+import * as Discord from 'discord.js';
+import getEmojiByName from './getEmojiByName.js';
+import readConfig from './readConfig.js';
+import { toHexArray } from './calculate.js';
+import { promisify } from 'util';
+import generateKeyValueList from './generateKeyValueList.js';
+
+const wait = promisify(setTimeout);
+
+
+export class Logger {
+ renderUser(user: Discord.User) {
+ return `${user.username} [<@${user.id}>]`;
+ }
+ renderTime(t: number) {
+ t = Math.floor(t /= 1000)
+ return `<t:${t}:D> at <t:${t}:T>`;
+ }
+ renderDelta(t: number) {
+ t = Math.floor(t /= 1000)
+ return `<t:${t}:R> (<t:${t}:D> at <t:${t}:T>)`;
+ }
+ renderNumberDelta(num1, num2) {
+ let delta = num2 - num1;
+ return `${num1} -> ${num2} (${delta > 0 ? '+' : ''}${delta})`;
+ }
+ entry(value, displayValue) {
+ return { value: value, displayValue: displayValue }
+ }
+ renderChannel(channel: Discord.GuildChannel) {
+ return `${channel.name} [<#${channel.id}>]`;
+ }
+ renderRole(role: Discord.Role) {
+ return `${role.name} [<@&${role.id}>]`;
+ }
+ renderEmoji(emoji: Discord.GuildEmoji) {
+ return `<${emoji.animated ? 'a' : ''}:${emoji.name}:${emoji.id}> [\`:${emoji.name}:\`]`;
+ }
+
+ public readonly NucleusColors = {
+ red: 0xF27878,
+ yellow: 0xF2D478,
+ green: 0x68D49E,
+
+ }
+
+
+ async getAuditLog(guild: Discord.Guild, event) {
+ await wait(250)
+ let auditLog = await guild.fetchAuditLogs({type: event});
+ return auditLog;
+ }
+
+ async log(log: any, client): Promise<void> {
+ let config = await readConfig(log.hidden.guild);
+ if (!config.logging.logs.enabled) return;
+ if (!(log.meta.calculateType == true)) if(!toHexArray(config.logging.logs.toLog).includes(log.meta.calculateType)) return console.log('Not logging this type of event');
+ if (config.logging.logs.channel) {
+ let channel = await client.channels.fetch(config.logging.logs.channel) as Discord.TextChannel;
+ let description = {};
+ Object.entries(log.list).map(entry => {
+ let key = entry[0];
+ let value:any = entry[1];
+ if(value.displayValue) {
+ description[key] = value.displayValue;
+ } else {
+ description[key] = value;
+ }
+ })
+ if (channel) {
+ log.separate = log.separate || {};
+ let embed = new Discord.MessageEmbed()
+ .setTitle(`${getEmojiByName(log.meta.emoji)} ${log.meta.displayName}`)
+ .setDescription(
+ (log.separate.start ? log.separate.start + "\n" : "") +
+ generateKeyValueList(description) +
+ (log.separate.end ? "\n" + log.separate.end : "")
+ )
+ .setTimestamp(log.meta.timestamp)
+ .setColor(log.meta.color);
+ channel.send({embeds: [embed]});
+ }
+ }
+ saveLog(log);
+ }
+}
+
+
+export default {}
+
+async function saveLog(log: any): Promise<void> {
+}
+
+export function readLogs(guild: string) {
+
+}
+
+export function readSpecificLog(guild: string, id: number) {
+
+}
diff --git a/src/utils/readConfig.ts b/src/utils/readConfig.ts
new file mode 100644
index 0000000..c53d2cc
--- /dev/null
+++ b/src/utils/readConfig.ts
@@ -0,0 +1,85 @@
+
+export default async function readConfig(guild: string): Promise<any> {
+
+ let config = {
+ filters: {
+ images: {
+ NSFW: true,
+ size: true
+ },
+ malware: true,
+ wordFilter: {
+ enabled: true,
+ words: {
+ strict: [],
+ loose: []
+ },
+ allowed: {
+ users: [],
+ roles: [],
+ channels: []
+ }
+ },
+ invite: {
+ enabled: false,
+ allowed: {
+ users: [],
+ channels: [],
+ roles: []
+ }
+ },
+ pings: {
+ mass: 5,
+ everyone: true,
+ roles: true,
+ allowed: {
+ roles: [],
+ rolesToMention: [],
+ users: [],
+ channels: []
+ }
+ }
+ },
+ welcome: {
+ enabled: true,
+ verificationRequired: {
+ message: false,
+ role: false
+ },
+ welcomeRole: null,
+ channel: '895209752315961344', // null, channel ID or 'dm'
+ message: "Welcome to the server, {@}!"
+ },
+ stats: [
+ {
+ enabled: true,
+ channel: '951910554291818526',
+ text: "{count} members | {count:bots} bots | {count:humans} humans"
+ }
+ ],
+ logging: {
+ logs: {
+ enabled: true,
+ channel: '952247098437427260',
+ toLog: "3fffff" // "3ffffe" = - channelUpdate, "3fffff" = all
+ },
+ staff: {}
+ },
+ verify: {
+ enabled: true,
+ channel: '895210691479355392',
+ role: '934941369137524816',
+ },
+ tickets: {
+ enabled: true,
+ category: "952302254302584932",
+ types: "3f",
+ customTypes: null,
+ supportRole: null,
+ maxTickets: 5
+ }
+ };
+
+ return config
+
+}
\ No newline at end of file
diff --git a/src/utils/scanners.ts b/src/utils/scanners.ts
new file mode 100644
index 0000000..97e9bf4
--- /dev/null
+++ b/src/utils/scanners.ts
@@ -0,0 +1,30 @@
+import * as us from 'unscan'
+import fetch from 'node-fetch'
+import { writeFileSync } from 'fs'
+import generateFileName from './temp/generateFileName.js'
+import * as path from 'path'
+import {fileURLToPath} from 'url';
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+export async function testNSFW(link: string): Promise<JSON> {
+ const image = (await (await fetch(link)).buffer()).toString('base64')
+ let fileName = generateFileName(link.split('/').pop().split('.').pop())
+ let p = path.join(__dirname, '/temp', fileName)
+ writeFileSync(p, image, 'base64')
+ let result = await us.nsfw.file(p)
+ return result
+}
+
+export async function testMalware(link: string): Promise<JSON> {
+ const file = (await (await fetch(link)).buffer()).toString('base64')
+ let fileName = generateFileName(link.split('/').pop().split('.').pop())
+ let p = path.join(__dirname, '/temp', fileName)
+ writeFileSync(p, file, 'base64')
+ let result = await us.malware.file(p)
+ return result
+}
+
+export async function testLink(link: string): Promise<JSON> {
+ return await us.link.scan(link)
+}
diff --git a/src/utils/temp/generateFileName.ts b/src/utils/temp/generateFileName.ts
new file mode 100644
index 0000000..98240a1
--- /dev/null
+++ b/src/utils/temp/generateFileName.ts
@@ -0,0 +1,11 @@
+import * as fs from 'fs';
+import * as crypto from 'crypto';
+
+export default function generateFileName(ending: string): string {
+ let fileName = crypto.randomBytes(35).toString('hex');
+ fileName = fileName.replace(/([a-zA-Z0-9]{8})/g, '$1-');
+ if (fs.existsSync(`./${fileName}`)) {
+ fileName = generateFileName(ending);
+ }
+ return fileName + '.' + ending;
+}
\ No newline at end of file