diff --git a/COPYING.md b/COPYING.md index 2a301ce..ceffdf6 100644 --- a/COPYING.md +++ b/COPYING.md @@ -1,6 +1,6 @@ # Nucleus by Clicks -## TLDR: +## In Short: You **may**: - Modify our code @@ -13,7 +13,29 @@ However, you **must**: - Mention the code is originally by Clicks, and include a link to our [website](https://clicks.codes) or [GitHub](https://github.com/clicksminuteper) - Release your project with the same license [GNU Affero General Public License V3.0](https://choosealicense.com/licenses/agpl-3.0/) -## The legal bit + +## How to: + +We hide the config file with our important data like the bot token. Below you can find a copy of `src/config/main.json`. + +```json +{ + "token": "your-token-here", + "developmentToken": "dev-token-here", + "managementGuildID": "your-management-guild-id-here", + "developmentGuildID": "your-development-guild-id-here", + "enableDevelopment": true, + "owners": [ + "your-discord-id", + ], + "verifySecret": "if making a verify command, this is the value that checks if requests are from the website", + "mongoUrl": "mongodb://your-mongo-ip-and-port", + "baseUrl": "your website url, e.g. https://clicks.codes", +} +``` + + +## The legal bit: ``` GNU AFFERO GENERAL PUBLIC LICENSE diff --git a/TODO.json b/TODO.json new file mode 100644 index 0000000..b4b2524 --- /dev/null +++ b/TODO.json @@ -0,0 +1,94 @@ +{ + "filters": { + "images": { + "NSFW": false, + "size": false + }, + "malware": false, + "wordFilter": { + "enabled": false, + "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": false, + "welcomeRole": null, + "channel": null, + "message": null + }, + "stats": [ + { + "enabled": true, + "channel": "9849175359", + "text": "{count} members | {count:bots} bots | {count:humans} humans" + } + ], + "moderation": { + "mute": { + "timeout": true, + "role": null, + "text": null, + "link": null + }, + "kick": { + "text": null, + "link": null + }, + "ban": { + "text": null, + "link": null + }, + "softban": { + "text": null, + "link": null + }, + "warn": { + "text": null, + "link": null + }, + "role": { + "role": null + } + }, + "tracks": [ + ], + "logging": { + "logs": { + "enabled": true, + "toLog": "3fffff" + } + }, + "roleMenu": { + "enabled": true, + "allowWebUI": true, + "options": [ + ] + } +} \ No newline at end of file diff --git a/src/api/index.ts b/src/api/index.ts index 00c866d..b2edc66 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -18,7 +18,6 @@ const runServer = (client: HaikuClient) => { app.post('/verify/:code', jsonParser, async function (req, res) { const code = req.params.code; const secret = req.body.secret; - const { log, NucleusColors, entry, renderUser } = client.logger if (secret === client.config.verifySecret) { let guild = await client.guilds.fetch(client.verify[code].gID); if (!guild) { return res.status(404) } @@ -36,6 +35,8 @@ const runServer = (client: HaikuClient) => { .setEmoji("MEMBER.JOIN") ], components: []}); } + delete client.verify[code]; + const { log, NucleusColors, entry, renderUser } = client.logger try { let data = { meta:{ @@ -47,7 +48,7 @@ const runServer = (client: HaikuClient) => { timestamp: new Date().getTime() }, list: { - id: entry(member.id, `\`${member.id}\``), + memberId: entry(member.id, `\`${member.id}\``), member: entry(member.id, renderUser(member)) }, hidden: { @@ -62,25 +63,20 @@ const runServer = (client: HaikuClient) => { } }); - app.patch('/verify/:code', (req, res) => { - const code = req.params.code; - try { - let interaction = client.verify[code].interaction; - if (interaction) { - interaction.editReply({embeds: [new EmojiEmbed() - .setTitle("Verify") - .setDescription(`Verify was opened in another tab or window, please complete the CAPTCHA there to continue`) - .setStatus("Success") - .setEmoji("MEMBER.JOIN") - ]}); - } - } catch {} - res.sendStatus(200); - }) - app.get('/verify/:code', jsonParser, function (req, res) { const code = req.params.code; if (client.verify[code]) { + try { + let interaction = client.verify[code].interaction; + if (interaction) { + interaction.editReply({embeds: [new EmojiEmbed() + .setTitle("Verify") + .setDescription(`Verify was opened in another tab or window, please complete the CAPTCHA there to continue`) + .setStatus("Success") + .setEmoji("MEMBER.JOIN") + ]}); + } + } catch {} let data = structuredClone(client.verify[code]) delete data.interaction; return res.status(200).send(data); @@ -88,6 +84,44 @@ const runServer = (client: HaikuClient) => { return res.sendStatus(404); }) + app.post('/rolemenu/:code', jsonParser, async function (req, res) { + const code = req.params.code; + const secret = req.body.secret; + const data = req.body.data; + if (secret === client.config.verifySecret) { + console.table(data) + let guild = await client.guilds.fetch(client.roleMenu[code].guild); // TODO: do checks here to like max roles because people are fucking annoying and will edit the source :) + if (!guild) { return res.status(404) } + let member = await guild.members.fetch(client.roleMenu[code].user); + if (!member) { return res.status(404) } + res.sendStatus(200); + } else { + res.sendStatus(403); + } + }); + + app.get('/rolemenu/:code', jsonParser, function (req, res) { + const code = req.params.code; + if (client.roleMenu[code] !== undefined) { + try { + let interaction = client.roleMenu[code].interaction; + if (interaction) { + interaction.editReply({embeds: [new EmojiEmbed() + .setTitle("Roles") + .setDescription(`The role menu was opened in another tab or window, please select your roles there to continue`) + .setStatus("Success") + .setEmoji("GUILD.GREEN") + ], components: []}); + } + } catch {} + let data = structuredClone(client.roleMenu[code]) + delete data.interaction; + console.log(data) + return res.status(200).send(data); + } + return res.sendStatus(404); + }) + app.listen(port); } diff --git a/src/automations/createModActionTicket.ts b/src/automations/createModActionTicket.ts index fad9a62..2ab9a46 100644 --- a/src/automations/createModActionTicket.ts +++ b/src/automations/createModActionTicket.ts @@ -54,7 +54,7 @@ export async function create(guild: Discord.Guild, member: Discord.User, created `Ticket created by a Moderator\n` + `**Support type:** Appeal submission\n` + (reason != null ? `**Reason:**\n> ${reason}\n` : "") + `**Ticket ID:** \`${c.id}\`\n` + - `Type \`/ticket close\` to archive this ticket.`, + `Type \`/ticket close\` to close this ticket.`, ) .setStatus("Success") .setEmoji("GUILD.TICKET.OPEN") diff --git a/src/automations/guide.ts b/src/automations/guide.ts index 8e0e6cd..655d781 100644 --- a/src/automations/guide.ts +++ b/src/automations/guide.ts @@ -37,8 +37,9 @@ export default async (guild, interaction?) => { "Nucleus can log server events and keep you informed with what content is being posted to your server.\n" + "We have 2 different types of logs, which each can be configured to send to a channel of your choice:\n" + "**General Logs:** These are events like kicks and channel changes etc.\n" + - "**Warning Logs:** Warnings like NSFW avatars and spam etc that may require action by a server staff member.\n\n" + - "A general log channel can be set with `/settings log channel`\n" + + "**Warning Logs:** Warnings like NSFW avatars and spam etc. that may require action by a server staff member. " + + "These go to to a separate staff notifications channel.\n\n" + + "A general log channel can be set with `/settings log`\n" + "A warning log channel can be set with `/settings warnings channel`" ) .setEmoji("NUCLEUS.LOGO") @@ -69,7 +70,7 @@ export default async (guild, interaction?) => { "Nucleus has a verification system that allows users to prove they aren't bots.\n" + "This is done by running `/verify` which sends a message only the user can see, giving them a link to a CAPTCHA to verify.\n" + "After the user complete's the CAPTCHA, they are given a role and can use the permissions accordingly.\n" + - "You can set the role given with `/settings verify role`" + "You can set the role given with `/settings verify`" ) .setEmoji("NUCLEUS.LOGO") .setStatus("Danger") @@ -80,7 +81,7 @@ export default async (guild, interaction?) => { .setDescription( "Nucleus has a content scanning system that automatically scans links and images sent by users.\n" + "Nucleus can detect, delete, and punish users for sending NSFW content, or links to scam or adult sites.\n" + - "You can set the threshold for this in `/settings automation`" + "You can set the threshold for this in `/settings automation`" // TODO ) .setEmoji("NUCLEUS.LOGO") .setStatus("Danger") @@ -91,7 +92,7 @@ export default async (guild, interaction?) => { .setDescription( "Nucleus has a ticket system that allows users to create tickets and have a support team respond to them.\n" + "Tickets can be created with `/ticket create` and a channel is created, pinging the user and support role.\n" + - "When the ticket is resolved, anyone can run `/ticket close` to archive it.\n" + + "When the ticket is resolved, anyone can run `/ticket close` (or click the button) to close it.\n" + "Running `/ticket close` again will delete the ticket." ) .setEmoji("NUCLEUS.LOGO") diff --git a/src/automations/roleMenu.ts b/src/automations/roleMenu.ts index e82c82c..318a0dd 100644 --- a/src/automations/roleMenu.ts +++ b/src/automations/roleMenu.ts @@ -44,8 +44,11 @@ export async function callback(interaction) { } client.roleMenu[code] = { guild: interaction.guild.id, + guildName: interaction.guild.name, guildIcon: interaction.guild.iconURL({format: "png"}), user: interaction.member.user.id, + username: interaction.member.user.username, + data: config.roleMenu.options, interaction: interaction }; m = await interaction.editReply({ @@ -58,7 +61,8 @@ export async function callback(interaction) { new MessageButton() .setLabel("Online") .setStyle("LINK") - .setURL(`https://clicks.codes/nuclues/rolemenu?code=${code}`), + .setDisabled(false) // TODO check if the server is up + .setURL(`${client.config.baseUrl}/nucleus/rolemenu?code=${code}`), new MessageButton() .setLabel("Manual") .setStyle("PRIMARY") diff --git a/src/automations/tickets/create.ts b/src/automations/tickets/create.ts index 8eee3b2..8d58493 100644 --- a/src/automations/tickets/create.ts +++ b/src/automations/tickets/create.ts @@ -167,7 +167,7 @@ export default async function (interaction) { `Ticket created by <@${interaction.member.user.id}>\n` + `**Support type:** ${chosenType != null ? (emoji) + " " + capitalize(chosenType) : "General"}\n` + `**Ticket ID:** \`${c.id}\`\n${content}\n` + - `Type \`/ticket close\` to archive this ticket.`, + `Type \`/ticket close\` to close this ticket.`, ) .setStatus("Success") .setEmoji("GUILD.TICKET.OPEN") diff --git a/src/automations/unscan.ts b/src/automations/unscan.ts index 940a1fe..d1fa7d4 100644 --- a/src/automations/unscan.ts +++ b/src/automations/unscan.ts @@ -12,9 +12,7 @@ export async function LinkCheck(message): Promise { } catch {} detections.push({tags: element.tags || [], safe: element.safe}) }); - console.log(1) await Promise.all(promises); - console.log(2) let types = [ "PHISHING", "DATING", "TRACKERS", "ADVERTISEMENTS", "FACEBOOK", "AMP", "FACEBOOK TRACKERS", "IP GRABBERS", "PORN", @@ -27,7 +25,6 @@ export async function LinkCheck(message): Promise { // if (!element.safe) return "UNSAFE" return undefined }).filter(element => element !== undefined) - console.log(detectionsTypes) return detectionsTypes.length > 0 } @@ -56,22 +53,22 @@ export async function MalwareCheck(element): Promise { } } -export function TestString(string, soft, strict): string { +export function TestString(string, soft, strict): object | null { for(let word of strict || []) { if (string.toLowerCase().includes(word)) { - return "strict" + return {word: word, type: "strict"} } } for(let word of soft) { for(let word2 of string.match(/[a-z]+/gi) || []) { if (word2 == word) { - return "loose" + return {word: word, type: "strict"} } } } - return "none" + return null } -export async function TestImage(element): Promise { - return ""; +export async function TestImage(element): Promise { + return null; } diff --git a/src/automations/verify.ts b/src/automations/verify.ts index ed24d25..710439a 100644 --- a/src/automations/verify.ts +++ b/src/automations/verify.ts @@ -88,7 +88,7 @@ export default async function(interaction) { .setStatus("Warning") .setEmoji("NUCLEUS.LOADING") ]}); - if (TestString((interaction.member as Discord.GuildMember).displayName, config.filters.wordFilter.words.loose, config.filters.wordFilter.words.strict) != "none") { + if (TestString((interaction.member as Discord.GuildMember).displayName, config.filters.wordFilter.words.loose, config.filters.wordFilter.words.strict) !== null) { return await interaction.editReply({embeds: [new EmojiEmbed() .setTitle("Verify") .setDescription(`Your name contained a word we do not allow in this server.\nPlease contact one of our staff members if you believe this is a mistake` + step(2)) @@ -124,7 +124,7 @@ export default async function(interaction) { gID: interaction.guild.id, rID: config.verify.role, rName: (await interaction.guild.roles.fetch(config.verify.role)).name, - mCount: interaction.guild.memberCount, + uName: interaction.member.user.username, gName: interaction.guild.name, gIcon: interaction.guild.iconURL({format: "png"}), interaction: interaction diff --git a/src/commands/mod/ban.ts b/src/commands/mod/ban.ts index 4581c0f..11c6c79 100644 --- a/src/commands/mod/ban.ts +++ b/src/commands/mod/ban.ts @@ -75,7 +75,7 @@ const callback = async (interaction: CommandInteraction): Promise => { timestamp: new Date().getTime() }, list: { - id: entry(member.user.id, `\`${member.user.id}\``), + memberId: entry(member.user.id, `\`${member.user.id}\``), name: entry(member.user.id, renderUser(member.user)), banned: entry(new Date().getTime(), renderDelta(new Date().getTime())), bannedBy: entry(interaction.user.id, renderUser(interaction.user)), diff --git a/src/commands/mod/kick.ts b/src/commands/mod/kick.ts index 97ead7b..b1464ee 100644 --- a/src/commands/mod/kick.ts +++ b/src/commands/mod/kick.ts @@ -71,7 +71,7 @@ const callback = async (interaction: CommandInteraction): Promise => { timestamp: new Date().getTime() }, list: { - id: entry(member.id, `\`${member.id}\``), + memberId: entry(member.id, `\`${member.id}\``), name: entry(member.id, renderUser(member.user)), joined: entry(member.joinedAt, renderDelta(member.joinedAt)), kicked: entry(new Date().getTime(), renderDelta(new Date().getTime())), diff --git a/src/commands/mod/mute.ts b/src/commands/mod/mute.ts index 5f42b27..4c326e7 100644 --- a/src/commands/mod/mute.ts +++ b/src/commands/mod/mute.ts @@ -163,7 +163,7 @@ const callback = async (interaction: CommandInteraction): Promise => { } if (config.moderation.mute.role) { member.roles.add(config.moderation.mute.role) - } + } // make sure this gets removed } catch { await interaction.editReply({embeds: [new EmojiEmbed() .setEmoji("PUNISH.MUTE.RED") diff --git a/src/commands/mod/nick.ts b/src/commands/mod/nick.ts index e154277..8dcbf62 100644 --- a/src/commands/mod/nick.ts +++ b/src/commands/mod/nick.ts @@ -73,7 +73,7 @@ const callback = async (interaction: CommandInteraction): Promise => { timestamp: new Date().getTime() }, list: { - id: entry(member.id, `\`${member.id}\``), + memberId: entry(member.id, `\`${member.id}\``), before: entry(before, before ? before : '*None*'), after: entry(nickname, nickname ? nickname : '*None*'), updated: entry(new Date().getTime(), renderDelta(new Date().getTime())), diff --git a/src/commands/mod/purge.ts b/src/commands/mod/purge.ts index 9aab260..fd8e6b8 100644 --- a/src/commands/mod/purge.ts +++ b/src/commands/mod/purge.ts @@ -144,7 +144,7 @@ const callback = async (interaction: CommandInteraction): Promise => { timestamp: new Date().getTime() }, list: { - id: entry(interaction.user.id, `\`${interaction.user.id}\``), + memberId: entry(interaction.user.id, `\`${interaction.user.id}\``), purgedBy: entry(interaction.user.id, renderUser(interaction.user)), channel: entry(interaction.channel.id, renderChannel(interaction.channel)), messagesCleared: entry(deleted.length, deleted.length), @@ -246,7 +246,7 @@ const callback = async (interaction: CommandInteraction): Promise => { timestamp: new Date().getTime() }, list: { - id: entry(interaction.user.id, `\`${interaction.user.id}\``), + memberId: entry(interaction.user.id, `\`${interaction.user.id}\``), purgedBy: entry(interaction.user.id, renderUser(interaction.user)), channel: entry(interaction.channel.id, renderChannel(interaction.channel)), messagesCleared: entry(messages.size, messages.size), diff --git a/src/commands/mod/unban.ts b/src/commands/mod/unban.ts index 7f605d9..e512084 100644 --- a/src/commands/mod/unban.ts +++ b/src/commands/mod/unban.ts @@ -53,7 +53,7 @@ const callback = async (interaction: CommandInteraction): Promise => { timestamp: new Date().getTime() }, list: { - id: entry(member.id, `\`${member.id}\``), + memberId: entry(member.id, `\`${member.id}\``), name: entry(member.id, renderUser(member)), unbanned: entry(new Date().getTime(), renderDelta(new Date().getTime())), unbannedBy: entry(interaction.user.id, renderUser(interaction.user)), diff --git a/src/commands/mod/viewas.ts b/src/commands/mod/viewas.ts index b3875a7..684fc3a 100644 --- a/src/commands/mod/viewas.ts +++ b/src/commands/mod/viewas.ts @@ -97,7 +97,6 @@ const callback = async (interaction: CommandInteraction): Promise => { const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => { - return true // FIXME FOR RELEASE let member = (interaction.member as GuildMember) if (! member.permissions.has("MANAGE_ROLES")) throw "You do not have the Manage roles permission"; return true diff --git a/src/commands/rolemenu.ts b/src/commands/rolemenu.ts new file mode 100644 index 0000000..f53e10a --- /dev/null +++ b/src/commands/rolemenu.ts @@ -0,0 +1,20 @@ +import { CommandInteraction } from "discord.js"; +import { SlashCommandBuilder } from "@discordjs/builders"; +import { WrappedCheck } from "jshaiku"; +import { callback as roleMenu } from "../automations/roleMenu.js" + +const command = new SlashCommandBuilder() + .setName("rolemenu") + .setDescription("Lets you choose from sets of roles to apply to yourself") + +const callback = async (interaction: CommandInteraction): Promise => { + await roleMenu(interaction); +} + +const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => { + return true; +} + +export { command }; +export { callback }; +export { check }; diff --git a/src/commands/settings/logs/channel.ts b/src/commands/settings/logs/channel.ts index 19a389b..10a5887 100644 --- a/src/commands/settings/logs/channel.ts +++ b/src/commands/settings/logs/channel.ts @@ -53,6 +53,28 @@ const callback = async (interaction: CommandInteraction): Promise => { if (confirmation.success) { try { await client.database.guilds.write(interaction.guild.id, {"logging.logs.channel": channel.id}) + const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger + try { + let data = { + meta:{ + type: 'logChannelUpdate', + displayName: 'Log Channel Changed', + calculateType: 'nucleusSettingsUpdated', + color: NucleusColors.yellow, + emoji: "CHANNEL.TEXT.EDIT", + timestamp: new Date().getTime() + }, + list: { + memberId: entry(interaction.user.id, `\`${interaction.user.id}\``), + changedBy: entry(interaction.user.id, renderUser(interaction.user)), + channel: entry(channel.id, renderChannel(channel)), + }, + hidden: { + guild: channel.guild.id + } + } + log(data); + } catch {} } catch (e) { console.log(e) return interaction.editReply({embeds: [new EmojiEmbed() @@ -120,7 +142,7 @@ const callback = async (interaction: CommandInteraction): Promise => { const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => { let member = (interaction.member as Discord.GuildMember) - if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the `manage_server` permission to use this command" + if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the Manage Server permission to use this command" return true; } diff --git a/src/commands/settings/logs/ignore.ts b/src/commands/settings/logs/ignore.ts index 12af085..3b81d42 100644 --- a/src/commands/settings/logs/ignore.ts +++ b/src/commands/settings/logs/ignore.ts @@ -103,14 +103,38 @@ const callback = async (interaction: CommandInteraction): Promise => { if (role) data.logging.logs.ignore.roles.concat([role.id]) if (interaction.options.getString("action") == "add") { await client.database.guilds.append(interaction.guild.id, data) + } else { + await client.database.guilds.remove(interaction.guild.id, data) } + const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger + try { + let data = { + meta:{ + type: 'logIgnoreUpdated', + displayName: 'Ignored Groups Changed', + calculateType: 'nucleusSettingsUpdated', + color: NucleusColors.yellow, + emoji: "CHANNEL.TEXT.EDIT", + timestamp: new Date().getTime() + }, + list: { + memberId: entry(interaction.user.id, `\`${interaction.user.id}\``), + changedBy: entry(interaction.user.id, renderUser(interaction.user)), + channel: entry(channel.id, renderChannel(channel)), + }, + hidden: { + guild: interaction.guild.id + } + } + log(data); + } catch {} } } } const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => { let member = (interaction.member as Discord.GuildMember) - if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the `manage_server` permission to use this command" + if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the Manage Server permission to use this command" return true; } diff --git a/src/commands/settings/rolemenu.ts b/src/commands/settings/rolemenu.ts new file mode 100644 index 0000000..ec54820 --- /dev/null +++ b/src/commands/settings/rolemenu.ts @@ -0,0 +1,26 @@ +import Discord, { CommandInteraction, MessageActionRow, MessageButton } from "discord.js"; +import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; +import confirmationMessage from "../../utils/confirmationMessage.js"; +import getEmojiByName from "../../utils/getEmojiByName.js"; +import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; +import { WrappedCheck } from "jshaiku"; +import client from "../../utils/client.js"; + +const command = (builder: SlashCommandSubcommandBuilder) => + builder + .setName("role") + .setDescription("Sets or shows the role given to users after using /verify") + .addRoleOption(option => option.setName("role").setDescription("The role to give after verifying")) + +const callback = async (interaction: CommandInteraction): Promise => { +} + +const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => { + let member = (interaction.member as Discord.GuildMember) + if (!member.permissions.has("MANAGE_ROLES")) throw "You must have the Manage roles permission to use this command" + return true; +} + +export { command }; +export { callback }; +export { check }; \ No newline at end of file diff --git a/src/commands/settings/staff/channel.ts b/src/commands/settings/staff.ts similarity index 78% rename from src/commands/settings/staff/channel.ts rename to src/commands/settings/staff.ts index 74605cf..e0d2776 100644 --- a/src/commands/settings/staff/channel.ts +++ b/src/commands/settings/staff.ts @@ -1,19 +1,19 @@ import { ChannelType } from 'discord-api-types'; import Discord, { CommandInteraction, MessageActionRow, MessageButton } from "discord.js"; -import EmojiEmbed from "../../../utils/generateEmojiEmbed.js"; -import confirmationMessage from "../../../utils/confirmationMessage.js"; -import getEmojiByName from "../../../utils/getEmojiByName.js"; +import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; +import confirmationMessage from "../../utils/confirmationMessage.js"; +import getEmojiByName from "../../utils/getEmojiByName.js"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { WrappedCheck } from "jshaiku"; -import client from "../../../utils/client.js"; +import client from "../../utils/client.js"; const command = (builder: SlashCommandSubcommandBuilder) => builder - .setName("channel") - .setDescription("Sets or shows the staff notifications channel") + .setName("staff") + .setDescription("Settings for the staff notifications channel") .addChannelOption(option => option.setName("channel").setDescription("The channel to set the staff notifications channel to").addChannelTypes([ ChannelType.GuildNews, ChannelType.GuildText - ])) + ]).setRequired(false)) const callback = async (interaction: CommandInteraction): Promise => { let m; @@ -56,6 +56,28 @@ const callback = async (interaction: CommandInteraction): Promise => { if (confirmation.success) { try { await client.database.guilds.write(interaction.guild.id, {"logging.staff.channel": channel.id}) + const { log, NucleusColors, entry, renderUser, renderChannel } = client.logger + try { + let data = { + meta:{ + type: 'logIgnoreUpdated', + displayName: 'Staff Notifications Channel Updated', + calculateType: 'nucleusSettingsUpdated', + color: NucleusColors.yellow, + emoji: "CHANNEL.TEXT.EDIT", + timestamp: new Date().getTime() + }, + list: { + memberId: entry(interaction.user.id, `\`${interaction.user.id}\``), + changedBy: entry(interaction.user.id, renderUser(interaction.user)), + channel: entry(channel.id, renderChannel(channel)), + }, + hidden: { + guild: interaction.guild.id + } + } + log(data); + } catch {} } catch (e) { return interaction.editReply({embeds: [new EmojiEmbed() .setTitle("Staff Notifications Channel") @@ -122,7 +144,7 @@ const callback = async (interaction: CommandInteraction): Promise => { const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => { let member = (interaction.member as Discord.GuildMember) - if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the `manage_server` permission to use this command" + if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the Manage Server permission to use this command" return true; } diff --git a/src/commands/settings/staff/_meta.ts b/src/commands/settings/staff/_meta.ts deleted file mode 100644 index 9c3cd62..0000000 --- a/src/commands/settings/staff/_meta.ts +++ /dev/null @@ -1,4 +0,0 @@ -const name = "warnings"; -const description = "Settings for mod warnings"; - -export { name, description }; \ No newline at end of file diff --git a/src/commands/settings/tickets.ts b/src/commands/settings/tickets.ts index b505c5c..e664bd0 100644 --- a/src/commands/settings/tickets.ts +++ b/src/commands/settings/tickets.ts @@ -404,7 +404,7 @@ async function manageTypes(interaction, data, m) { const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => { let member = (interaction.member as Discord.GuildMember) - if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the `manage_server` permission to use this command" + if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the Manage Server permission to use this command" return true; } diff --git a/src/commands/settings/verify/role.ts b/src/commands/settings/verify.ts similarity index 76% rename from src/commands/settings/verify/role.ts rename to src/commands/settings/verify.ts index 44406e9..7a68c64 100644 --- a/src/commands/settings/verify/role.ts +++ b/src/commands/settings/verify.ts @@ -1,16 +1,16 @@ import Discord, { CommandInteraction, MessageActionRow, MessageButton } from "discord.js"; -import EmojiEmbed from "../../../utils/generateEmojiEmbed.js"; -import confirmationMessage from "../../../utils/confirmationMessage.js"; -import getEmojiByName from "../../../utils/getEmojiByName.js"; +import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; +import confirmationMessage from "../../utils/confirmationMessage.js"; +import getEmojiByName from "../../utils/getEmojiByName.js"; import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import { WrappedCheck } from "jshaiku"; -import client from "../../../utils/client.js"; +import client from "../../utils/client.js"; const command = (builder: SlashCommandSubcommandBuilder) => builder - .setName("role") - .setDescription("Sets or shows the role given to users after using /verify") - .addRoleOption(option => option.setName("role").setDescription("The role to give after verifying")) + .setName("verify") + .setDescription("Manage the role given after typing /verify") + .addRoleOption(option => option.setName("role").setDescription("The role to give after verifying").setRequired(false)) const callback = async (interaction: CommandInteraction): Promise => { let m; @@ -50,6 +50,28 @@ const callback = async (interaction: CommandInteraction): Promise => { if (confirmation.success) { try { await client.database.guilds.write(interaction.guild.id, {"verify.role": role.id, "verify.enabled": true}); + const { log, NucleusColors, entry, renderUser, renderRole } = client.logger + try { + let data = { + meta:{ + type: 'verifyRoleChanged', + displayName: 'Ignored Groups Changed', + calculateType: 'nucleusSettingsUpdated', + color: NucleusColors.green, + emoji: "CONTROL.BLOCKTICK", + timestamp: new Date().getTime() + }, + list: { + memberId: entry(interaction.user.id, `\`${interaction.user.id}\``), + changedBy: entry(interaction.user.id, renderUser(interaction.user)), + role: entry(role.id, renderRole(role)), + }, + hidden: { + guild: interaction.guild.id + } + } + log(data); + } catch {} } catch (e) { console.log(e) return interaction.editReply({embeds: [new EmojiEmbed() @@ -117,7 +139,7 @@ const callback = async (interaction: CommandInteraction): Promise => { const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => { let member = (interaction.member as Discord.GuildMember) - if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the `manage_server` permission to use this command" + if (!member.permissions.has("MANAGE_GUILD")) throw "You must have the Manage Server permission to use this command" return true; } diff --git a/src/commands/settings/verify/_meta.ts b/src/commands/settings/verify/_meta.ts deleted file mode 100644 index 999c50b..0000000 --- a/src/commands/settings/verify/_meta.ts +++ /dev/null @@ -1,4 +0,0 @@ -const name = "verify"; -const description = "Settings for verification"; - -export { name, description }; \ No newline at end of file diff --git a/src/commands/tags/create.ts b/src/commands/tags/create.ts index c1ed839..6dbecf4 100644 --- a/src/commands/tags/create.ts +++ b/src/commands/tags/create.ts @@ -78,7 +78,7 @@ const callback = async (interaction: CommandInteraction): Promise => { const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => { let member = (interaction.member as Discord.GuildMember) - if (!member.permissions.has("MANAGE_MESSAGES")) throw "You must have the `manage_messages` permission to use this command" + if (!member.permissions.has("MANAGE_MESSAGES")) throw "You must have the Manage Messages permission to use this command" return true; } diff --git a/src/commands/tags/delete.ts b/src/commands/tags/delete.ts index f24a5fc..2707465 100644 --- a/src/commands/tags/delete.ts +++ b/src/commands/tags/delete.ts @@ -60,7 +60,7 @@ const callback = async (interaction: CommandInteraction): Promise => { const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => { let member = (interaction.member as Discord.GuildMember) - if (!member.permissions.has("MANAGE_MESSAGES")) throw "You must have the `manage_messages` permission to use this command" + if (!member.permissions.has("MANAGE_MESSAGES")) throw "You must have the Manage Messages permission to use this command" return true; } diff --git a/src/commands/tags/edit.ts b/src/commands/tags/edit.ts index b0a4835..77e21ae 100644 --- a/src/commands/tags/edit.ts +++ b/src/commands/tags/edit.ts @@ -93,7 +93,7 @@ const callback = async (interaction: CommandInteraction): Promise => { const check = (interaction: CommandInteraction, defaultCheck: WrappedCheck) => { let member = (interaction.member as Discord.GuildMember) - if (!member.permissions.has("MANAGE_MESSAGES")) throw "You must have the `manage_messages` permission to use this command" + if (!member.permissions.has("MANAGE_MESSAGES")) throw "You must have the Manage Messages permission to use this command" return true; } diff --git a/src/config/emojis.json b/src/config/emojis.json index f6eabe0..e9fb578 100644 --- a/src/config/emojis.json +++ b/src/config/emojis.json @@ -119,6 +119,7 @@ "DELETE": "752070251948146768" }, "MESSAGE": { + "CREATE": "751762088229339136", "EDIT": "729065958584614925", "DELETE": "729064530432360461", "PIN": "729064530755190894", diff --git a/src/events/channelCreate.ts b/src/events/channelCreate.ts index 3bde0f2..2d48255 100644 --- a/src/events/channelCreate.ts +++ b/src/events/channelCreate.ts @@ -55,7 +55,7 @@ export async function callback(client, channel) { timestamp: channel.createdTimestamp }, list: { - id: entry(channel.id, `\`${channel.id}\``), + channelId: 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"), diff --git a/src/events/channelDelete.ts b/src/events/channelDelete.ts index 2188141..5c7139a 100644 --- a/src/events/channelDelete.ts +++ b/src/events/channelDelete.ts @@ -39,7 +39,7 @@ export async function callback(client, channel) { } } let list = { - id: entry(channel.id, `\`${channel.id}\``), + channelIid: entry(channel.id, `\`${channel.id}\``), name: entry(channel.id, `${channel.name}`), topic: null, type: entry(channel.type, readableType), diff --git a/src/events/channelUpdate.ts b/src/events/channelUpdate.ts index 560ca11..50a3de1 100644 --- a/src/events/channelUpdate.ts +++ b/src/events/channelUpdate.ts @@ -18,7 +18,7 @@ export async function callback(client, oc, nc) { let readableType:string; let displayName:string ; let changes = { - id: entry(nc.id, `\`${nc.id}\``), + channelId: entry(nc.id, `\`${nc.id}\``), channel: entry(nc.id, renderChannel(nc)), edited: entry(new Date().getTime(), renderDelta(new Date().getTime())), editedBy: entry(audit.executor.id, renderUser((await nc.guild.members.fetch(audit.executor.id)).user)), diff --git a/src/events/emojiCreate.ts b/src/events/emojiCreate.ts index 1048869..25d1b62 100644 --- a/src/events/emojiCreate.ts +++ b/src/events/emojiCreate.ts @@ -16,7 +16,7 @@ export async function callback(client, emoji) { timestamp: emoji.createdTimestamp }, list: { - id: entry(emoji.id, `\`${emoji.id}\``), + emojiId: 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)) diff --git a/src/events/emojiDelete.ts b/src/events/emojiDelete.ts index 02bec0d..2dcb685 100644 --- a/src/events/emojiDelete.ts +++ b/src/events/emojiDelete.ts @@ -16,7 +16,7 @@ export async function callback(client, emoji) { timestamp: audit.createdTimestamp, }, list: { - id: entry(emoji.id, `\`${emoji.id}\``), + emojiId: 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)), diff --git a/src/events/emojiUpdate.ts b/src/events/emojiUpdate.ts index eb39cb9..79fb171 100644 --- a/src/events/emojiUpdate.ts +++ b/src/events/emojiUpdate.ts @@ -12,7 +12,7 @@ export async function callback(client, oe, ne) { if (audit.executor.id == client.user.id) return; let changes = { - id: entry(ne.id, `\`${ne.id}\``), + emojiId: 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)), diff --git a/src/events/guildBanAdd.ts b/src/events/guildBanAdd.ts index a1848fd..05b98ed 100644 --- a/src/events/guildBanAdd.ts +++ b/src/events/guildBanAdd.ts @@ -22,7 +22,7 @@ export async function callback(client, ban) { timestamp: new Date().getTime() }, list: { - id: entry(ban.user.id, `\`${ban.user.id}\``), + memberId: entry(ban.user.id, `\`${ban.user.id}\``), name: entry(ban.user.id, renderUser(ban.user)), banned: entry(new Date().getTime(), renderDelta(new Date().getTime())), bannedBy: entry(audit.executor.id, renderUser(audit.executor)), diff --git a/src/events/guildBanRemove.ts b/src/events/guildBanRemove.ts index 59fbf7a..85dc2af 100644 --- a/src/events/guildBanRemove.ts +++ b/src/events/guildBanRemove.ts @@ -23,7 +23,7 @@ export async function callback(client, ban) { timestamp: new Date().getTime() }, list: { - id: entry(ban.user.id, `\`${ban.user.id}\``), + memberId: entry(ban.user.id, `\`${ban.user.id}\``), name: entry(ban.user.id, renderUser(ban.user)), unbanned: entry(new Date().getTime(), renderDelta(new Date().getTime())), unbannedBy: entry(audit.executor.id, renderUser(audit.executor)), diff --git a/src/events/guildMemberUpdate.ts b/src/events/guildMemberUpdate.ts index 2175aaf..dab89fa 100644 --- a/src/events/guildMemberUpdate.ts +++ b/src/events/guildMemberUpdate.ts @@ -23,7 +23,7 @@ export async function callback(client, before, after) { timestamp: new Date().getTime() }, list: { - id: entry(after.id, `\`${after.id}\``), + memberId: entry(after.id, `\`${after.id}\``), before: entry(before.nickname, before.nickname ? before.nickname : '*None*'), after: entry(after.nickname, after.nickname ? after.nickname : '*None*'), updated: entry(new Date().getTime(), renderDelta(new Date().getTime())), diff --git a/src/events/memberJoin.ts b/src/events/memberJoin.ts index f3c4d18..2031b12 100644 --- a/src/events/memberJoin.ts +++ b/src/events/memberJoin.ts @@ -21,7 +21,7 @@ export async function callback(_, member) { timestamp: member.joinedTimestamp }, list: { - id: entry(member.id, `\`${member.id}\``), + memberId: 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)), diff --git a/src/events/memberLeave.ts b/src/events/memberLeave.ts index 43e78bd..b9d6e84 100644 --- a/src/events/memberLeave.ts +++ b/src/events/memberLeave.ts @@ -31,7 +31,7 @@ export async function callback(client, member) { timestamp: new Date().getTime() }, list: { - id: entry(member.id, `\`${member.id}\``), + memberId: entry(member.id, `\`${member.id}\``), name: entry(member.id, renderUser(member.user)), joined: entry(member.joinedAt, renderDelta(member.joinedAt)), kicked: entry(new Date().getTime(), renderDelta(new Date().getTime())), @@ -57,7 +57,7 @@ export async function callback(client, member) { timestamp: new Date().getTime() }, list: { - id: entry(member.id, `\`${member.id}\``), + memberId: entry(member.id, `\`${member.id}\``), name: entry(member.id, renderUser(member.user)), joined: entry(member.joinedTimestamp, renderDelta(member.joinedAt)), left: entry(new Date().getTime(), renderDelta(new Date().getTime())), diff --git a/src/events/messageChecks.ts b/src/events/messageChecks.ts index 4e99c61..2755ff1 100644 --- a/src/events/messageChecks.ts +++ b/src/events/messageChecks.ts @@ -1,9 +1,11 @@ import { LinkCheck, MalwareCheck, NSFWCheck, SizeCheck, TestString, TestImage } from '../automations/unscan.js' import { Message } from 'discord.js' +import client from '../utils/client.js' export const event = 'messageCreate' export async function callback(client, message) { + const { log, NucleusColors, entry, renderUser } = client.logger if (message.author.bot) return if (message.channel.type === 'dm') return @@ -16,8 +18,7 @@ export async function callback(client, message) { !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]+\/?/)) + return message.delete(); } } } @@ -34,22 +35,19 @@ export async function callback(client, message) { 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) + return await message.delete() } } 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) + return await message.delete() } } } if (config.filters.malware) { if (!MalwareCheck(url)) { - await message.delete() - return toLog(message, 'malware', url) + return await message.delete() } } } @@ -58,15 +56,13 @@ export async function callback(client, message) { if(!message) return; if (await LinkCheck(message)) { - await message.delete() - return toLog(message, 'link') + return await message.delete() } 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(check !== null) { + return await message.delete() } } @@ -75,26 +71,19 @@ export async function callback(client, message) { !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') + return message.delete(); } 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') + return message.delete(); } } } if(!message) return; if (message.mentions.users.size >= config.filters.pings.mass && config.filters.pings.mass) { - message.delete(); - return toLog(message, 'Mass Pings') + return message.delete(); } } } - -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 index 229b126..f0fbb57 100644 --- a/src/events/messageDelete.ts +++ b/src/events/messageDelete.ts @@ -25,7 +25,7 @@ export async function callback(client, message) { start: content ? `**Message:**\n\`\`\`${content}\`\`\`` : '**Message:** *Message had no content*', }, list: { - id: entry(message.id, `\`${message.id}\``), + messageId: 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().getTime(), renderDelta(new Date().getTime())), diff --git a/src/events/messageEdit.ts b/src/events/messageEdit.ts index 0d42fb9..3502963 100644 --- a/src/events/messageEdit.ts +++ b/src/events/messageEdit.ts @@ -7,7 +7,37 @@ export async function callback(client, oldMessage, newMessage) { newMessage.reference = newMessage.reference || {} let newContent = newMessage.cleanContent.replaceAll("`", "‘") let oldContent = oldMessage.cleanContent.replaceAll("`", "‘") - if (newContent == oldContent) return; + if (newContent == oldContent) { + if (!oldMessage.flags.has("CROSSPOSTED") && newMessage.flags.has("CROSSPOSTED")) { + let data = { + meta: { + type: 'messageAnnounce', + displayName: 'Message Published', + calculateType: 'messageAnnounce', + color: NucleusColors.yellow, + emoji: 'MESSAGE.CREATE', + timestamp: newMessage.editedTimestamp + }, + separate: { + end: `[[Jump to message]](${newMessage.url})` + }, + list: { + messageId: 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))), + published: 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) + }, + hidden: { + guild: newMessage.channel.guild.id + } + } + log(data); + } + return + }; if (newContent.length > 256) newContent = newContent.substring(0, 253) + '...' if (oldContent.length > 256) oldContent = oldContent.substring(0, 253) + '...' let data = { @@ -25,7 +55,7 @@ export async function callback(client, oldMessage, newMessage) { end: `[[Jump to message]](${newMessage.url})` }, list: { - id: entry(newMessage.id, `\`${newMessage.id}\``), + messageId: 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))), diff --git a/src/events/roleCreate.ts b/src/events/roleCreate.ts index 61d0a87..6fded26 100644 --- a/src/events/roleCreate.ts +++ b/src/events/roleCreate.ts @@ -17,7 +17,7 @@ export async function callback(client, role) { timestamp: role.createdTimestamp }, list: { - id: entry(role.id, `\`${role.id}\``), + roleId: 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)) diff --git a/src/events/roleDelete.ts b/src/events/roleDelete.ts index b0cb0f7..6a23d9e 100644 --- a/src/events/roleDelete.ts +++ b/src/events/roleDelete.ts @@ -19,7 +19,7 @@ export async function callback(client, role) { timestamp: audit.createdTimestamp, }, list: { - id: entry(role.id, `\`${role.id}\``), + roleId: 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`), diff --git a/src/events/roleUpdate.ts b/src/events/roleUpdate.ts index 982d915..baf4399 100644 --- a/src/events/roleUpdate.ts +++ b/src/events/roleUpdate.ts @@ -11,7 +11,7 @@ export async function callback(client, or, nr) { if (audit.executor.id == client.user.id) return; let changes = { - id: entry(nr.id, `\`${nr.id}\``), + roleId: 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)), diff --git a/src/events/stickerCreate.ts b/src/events/stickerCreate.ts new file mode 100644 index 0000000..b9511b0 --- /dev/null +++ b/src/events/stickerCreate.ts @@ -0,0 +1,30 @@ +export const event = 'stickerCreate' + +export async function callback(client, emoji) { + try { + const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta } = emoji.client.logger + let auditLog = await getAuditLog(emoji.guild, 'STICKER_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: 'stickerCreate', + displayName: 'Sticker Created', + calculateType: 'stickerUpdate', + color: NucleusColors.green, + emoji: "GUILD.EMOJI.CREATE", + timestamp: emoji.createdTimestamp + }, + list: { + stickerId: entry(emoji.id, `\`${emoji.id}\``), + emoji: entry(emoji.name, `\`:${emoji.name}:\``), + createdBy: entry(audit.executor.id, renderUser(audit.executor)), + created: entry(emoji.createdTimestamp, renderDelta(emoji.createdTimestamp)) + }, + hidden: { + guild: emoji.guild.id + } + } + log(data); + } catch {} +} diff --git a/src/events/stickerDelete.ts b/src/events/stickerDelete.ts new file mode 100644 index 0000000..7490851 --- /dev/null +++ b/src/events/stickerDelete.ts @@ -0,0 +1,31 @@ +export const event = 'stickerDelete' + +export async function callback(client, emoji) { + try{ + const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta } = emoji.client.logger + let auditLog = await getAuditLog(emoji.guild, 'STICKER_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: 'stickerDelete', + displayName: 'Sticker Deleted', + calculateType: 'stickerUpdate', + color: NucleusColors.red, + emoji: "GUILD.EMOJI.DELETE", + timestamp: audit.createdTimestamp, + }, + list: { + stickerId:entry(emoji.id, `\`${emoji.id}\``), + sticker: entry(emoji.name, `\`${emoji.name}\``), + 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); + } catch {} +} diff --git a/src/events/stickerUpdate.ts b/src/events/stickerUpdate.ts new file mode 100644 index 0000000..7cb745f --- /dev/null +++ b/src/events/stickerUpdate.ts @@ -0,0 +1,34 @@ +export const event = 'stickerUpdate'; + +export async function callback(client, oe, ne) { + try { + 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 = { + stickerId:entry(ne.id, `\`${ne.id}\``), + 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: 'stickerUpdate', + displayName: 'Sticker Edited', + calculateType: 'stickerUpdate', + color: NucleusColors.yellow, + emoji: "GUILD.EMOJI.EDIT", + timestamp: audit.createdTimestamp + }, + list: changes, + hidden: { + guild: ne.guild.id + } + } + log(data); + } catch {} +} \ No newline at end of file diff --git a/src/events/threadCreate.ts b/src/events/threadCreate.ts index 5c65d78..ff69e35 100644 --- a/src/events/threadCreate.ts +++ b/src/events/threadCreate.ts @@ -17,7 +17,7 @@ export async function callback(client, thread) { timestamp: thread.createdTimestamp }, list: { - id: entry(thread.id, `\`${thread.id}\``), + threadId:entry(thread.id, `\`${thread.id}\``), name: entry(thread.name, renderChannel(thread)), parentChannel: entry(thread.parentId, renderChannel(thread.parent)), category: entry(thread.parent.parent ? thread.parent.parent.name : 'None', thread.parent.parent ? renderChannel(thread.parent.parent) : 'None'), diff --git a/src/events/threadDelete.ts b/src/events/threadDelete.ts index 9507469..14c5820 100644 --- a/src/events/threadDelete.ts +++ b/src/events/threadDelete.ts @@ -17,7 +17,7 @@ export async function callback(client, thread) { timestamp: new Date().getTime() }, list: { - id: entry(thread.id, `\`${thread.id}\``), + threadId:entry(thread.id, `\`${thread.id}\``), name: entry(thread.name, thread.name), parentChannel: entry(thread.parentId, renderChannel(thread.parent)), category: entry(thread.parent.parent ? thread.parent.parent.name : 'None', thread.parent.parent ? renderChannel(thread.parent.parent) : 'None'), diff --git a/src/events/threadUpdate.ts b/src/events/threadUpdate.ts index f554a6c..4e13193 100644 --- a/src/events/threadUpdate.ts +++ b/src/events/threadUpdate.ts @@ -8,7 +8,7 @@ export async function callback(client, before, after) { let audit = auditLog.entries.filter(entry => entry.target.id == after.id).first(); if (audit.executor.id == client.user.id) return; let list = { - id: entry(after.id, `\`${after.id}\``), + threadId:entry(after.id, `\`${after.id}\``), thread: entry(after.name, renderChannel(after)), parentChannel: entry(after.parentId, renderChannel(after.parent)), } diff --git a/src/events/webhookUpdate.ts b/src/events/webhookUpdate.ts new file mode 100644 index 0000000..a20608c --- /dev/null +++ b/src/events/webhookUpdate.ts @@ -0,0 +1,78 @@ +import humanizeDuration from 'humanize-duration'; +export const event = 'webhookUpdate' + +export async function callback(client, channel) { + try { + const { getAuditLog, log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger + let auditLogCreate = getAuditLog(channel.guild, 'WEBHOOK_CREATE'); + let auditLogUpdate = getAuditLog(channel.guild, 'WEBHOOK_UPDATE'); + let auditLogDelete = getAuditLog(channel.guild, 'WEBHOOK_DELETE'); + [auditLogCreate, auditLogUpdate, auditLogDelete] = await Promise.all([auditLogCreate, auditLogUpdate, auditLogDelete]); + let auditCreate = auditLogCreate.entries.filter(entry => entry.target.channelId == channel.id).first(); + let auditUpdate = auditLogUpdate.entries.filter(entry => entry.target.channelId == channel.id).first(); + let auditDelete = auditLogDelete.entries.filter(entry => entry.target.channelId == channel.id).first(); + if (!auditCreate && !auditUpdate && !auditDelete) return; + let audit = auditCreate; + let action = "Create"; + let list = {} as any; + if (auditUpdate && auditUpdate.createdTimestamp > audit.createdTimestamp) { + let {before, after} = auditUpdate.changes.reduce( + (acc, change) => { acc.before[change.key] = change.old; acc.after[change.key] = change.new; return acc; }, + {before: {}, after: {}} + ); + if (before.name !== after.name) list['name'] = entry([before.name, after.name], `${before.name} -> ${after.name}`) + if (before.channel_id !== after.channel_id) list['channel'] = entry([before.channel_id, after.channel_id], renderChannel(await client.channels.fetch(before.channel_id)) + ` -> ` + renderChannel(await client.channels.fetch(after.channel_id))) + if (!(Object.keys(list)).length) return; + list.created = entry(auditUpdate.target.createdTimestamp, renderDelta(auditUpdate.target.createdTimestamp)); + list.edited = entry(after.editedTimestamp, renderDelta(new Date().getTime())); + list.editedBy = entry(auditUpdate.executor.id, renderUser(auditUpdate.executor)); + audit = auditUpdate; + action = "Update" + } else if (auditDelete && auditDelete.createdTimestamp > audit.createdTimestamp) { + let {before, after} = auditDelete.changes.reduce( + (acc, change) => { acc.before[change.key] = change.old; acc.after[change.key] = change.new; return acc; }, + {before: {}, after: {}} + ); + list = { + name: entry(before.name, `${before.name}`), + channel: entry(before.channel_id, renderChannel(await client.channels.fetch(before.channel_id))), + created: entry(auditDelete.target.createdTimestamp, renderDelta(auditDelete.target.createdTimestamp)), + deleted: entry(new Date().getTime(), renderDelta(new Date().getTime())), + deletedBy: entry(auditDelete.executor.id, renderUser((await channel.guild.members.fetch(auditDelete.executor.id)).user)), + } + audit = auditDelete; + action = "Delete" + } else { + let {before, after} = auditDelete.changes.reduce( + (acc, change) => { acc.before[change.key] = change.old; acc.after[change.key] = change.new; return acc; }, + {before: {}, after: {}} + ); + list = { + name: entry(before.name, `${before.name}`), + channel: entry(before.channel_id, renderChannel(await client.channels.fetch(before.channel_id))), + createdBy: entry(auditCreate.executor.id, renderUser((await channel.guild.members.fetch(auditCreate.executor.id)).user)), + created: entry(new Date().getTime(), renderDelta(new Date().getTime())), + } + } + let cols = { + "Create": "green", + "Update": "yellow", + "Delete": "red", + } + let data = { + meta: { + type: 'webhook' + action, + displayName: `Webhook ${action}d`, + calculateType: 'webhookUpdate', + color: NucleusColors[cols[action]], + emoji: "WEBHOOK." + action.toUpperCase(), + timestamp: new Date().getTime() + }, + list: list, + hidden: { + guild: channel.guild.id + } + } // TODO + log(data); + } catch(e) { console.log(e) } +} diff --git a/src/utils/calculate.ts b/src/utils/calculate.ts index a58fae1..7f3b16d 100644 --- a/src/utils/calculate.ts +++ b/src/utils/calculate.ts @@ -1,8 +1,7 @@ const logs = [ "channelUpdate", - "channelPinsUpdate", // TODO "emojiUpdate", - "stickerUpdate", // TODO + "stickerUpdate", "guildUpdate", "guildMemberUpdate", "guildMemberPunish", @@ -14,13 +13,12 @@ const logs = [ "messageReactionUpdate", "messagePing", "messageMassPing", - "messageAnnounce", // TODO + "messageAnnounce", "threadUpdate", - "webhookUpdate", // TODO + "webhookUpdate", "guildMemberVerify", - "autoModeratorDeleted", // TODO - "nucleusSettingsUpdated", // TODO - "" + "autoModeratorDeleted", // TODO: Not implemented + "nucleusSettingsUpdated" ] const tickets = [ @@ -51,7 +49,7 @@ const toHexArray = (permissionsHex, array?) => { let permissions = []; let int = (BigInt("0x" + permissionsHex)).toString(2).split('').reverse(); for (let index in int) { - if (int[index] == "1") { + if (int[index] == "1" && array.length > index) { permissions.push(array[index]); } } diff --git a/src/utils/log.ts b/src/utils/log.ts index 55f8f4c..987e256 100644 --- a/src/utils/log.ts +++ b/src/utils/log.ts @@ -46,10 +46,10 @@ export class Logger { } - async getAuditLog(guild: Discord.Guild, event) { + async getAuditLog(guild: Discord.Guild, event): Promise{ await wait(250) let auditLog = await guild.fetchAuditLogs({type: event}); - return auditLog; + return auditLog as unknown as Discord.GuildAuditLogsEntry[]; } async log(log: any): Promise {