diff --git a/package.json b/package.json index d4a4025..affb107 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "immutable": "^4.1.0", "mongodb": "^4.7.0", "node-cron": "^3.0.0", + "node-fetch": "^3.3.0", "node-tesseract-ocr": "^2.2.1", "pastebin-api": "^5.1.1", "structured-clone": "^0.2.2", diff --git a/src/commands/help.ts b/src/commands/help.ts index 4295f9c..e85cf6b 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -1,7 +1,9 @@ -import { CommandInteraction } from "discord.js"; +import type { CommandInteraction } from "discord.js"; import { SlashCommandBuilder } from "@discordjs/builders"; -const command = new SlashCommandBuilder().setName("help").setDescription("Shows help for commands"); +const command = new SlashCommandBuilder() + .setName("help") + .setDescription("Shows help for commands"); const callback = async (interaction: CommandInteraction): Promise => { interaction.reply("hel p D:"); // TODO: FINISH THIS FOR RELEASE diff --git a/src/commands/verify.ts b/src/commands/verify.ts index b9556a6..bf6a306 100644 --- a/src/commands/verify.ts +++ b/src/commands/verify.ts @@ -5,6 +5,7 @@ import verify from "../reflex/verify.js"; const command = new SlashCommandBuilder().setName("verify").setDescription("Get verified in the server"); const callback = async (interaction: CommandInteraction): Promise => { + interaction.reply("boo") verify(interaction); }; diff --git a/src/config/default.json b/src/config/default.json index 7f2fdbd..824f12c 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -50,6 +50,7 @@ } }, "verify": { + "enabled": false, "role": null }, "tickets": { diff --git a/src/events/channelCreate.ts b/src/events/channelCreate.ts index f3a4785..b3cba33 100644 --- a/src/events/channelCreate.ts +++ b/src/events/channelCreate.ts @@ -1,6 +1,6 @@ import type { GuildAuditLogsEntry } from "discord.js"; import type { GuildBasedChannel } from "discord.js"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; export const event = "channelCreate"; export async function callback(client: NucleusClient, channel: GuildBasedChannel) { diff --git a/src/events/channelDelete.ts b/src/events/channelDelete.ts index d92c994..2c48b02 100644 --- a/src/events/channelDelete.ts +++ b/src/events/channelDelete.ts @@ -6,12 +6,12 @@ import { ThreadChannel, VoiceChannel } from "discord.js"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import getEmojiByName from "../utils/getEmojiByName.js"; export const event = "channelDelete"; -export async function callback(client: HaikuClient, channel: GuildBasedChannel) { +export async function callback(client: NucleusClient, channel: GuildBasedChannel) { const { getAuditLog, log, NucleusColors, entry, renderDelta, renderUser } = client.logger; const auditLog = await getAuditLog(channel.guild, "CHANNEL_DELETE"); diff --git a/src/events/guildBanAdd.ts b/src/events/guildBanAdd.ts index d1649a3..5fd0b49 100644 --- a/src/events/guildBanAdd.ts +++ b/src/events/guildBanAdd.ts @@ -1,11 +1,11 @@ import type { GuildAuditLogsEntry, GuildBan } from "discord.js"; import { purgeByUser } from "../actions/tickets/delete.js"; import { callback as statsChannelRemove } from "../reflex/statsChannelUpdate.js"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; export const event = "guildBanAdd"; -export async function callback(client: HaikuClient, ban: GuildBan) { +export async function callback(client: NucleusClient, ban: GuildBan) { await statsChannelRemove(client, undefined, ban.guild, ban.user); purgeByUser(ban.user.id, ban.guild); const { log, NucleusColors, entry, renderUser, renderDelta, getAuditLog } = client.logger; diff --git a/src/events/guildBanRemove.ts b/src/events/guildBanRemove.ts index 2c3fc7b..f9b427a 100644 --- a/src/events/guildBanRemove.ts +++ b/src/events/guildBanRemove.ts @@ -1,10 +1,10 @@ import type { GuildAuditLogsEntry, GuildBan } from "discord.js"; import { purgeByUser } from "../actions/tickets/delete.js"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; export const event = "guildBanRemove"; -export async function callback(client: HaikuClient, ban: GuildBan) { +export async function callback(client: NucleusClient, ban: GuildBan) { purgeByUser(ban.user.id, ban.guild); const { log, NucleusColors, entry, renderUser, renderDelta, getAuditLog } = client.logger; const auditLog = await getAuditLog(ban.guild, "MEMBER_BAN_REMOVE"); diff --git a/src/events/guildCreate.ts b/src/events/guildCreate.ts index b1adbaa..ff6c6b2 100644 --- a/src/events/guildCreate.ts +++ b/src/events/guildCreate.ts @@ -1,9 +1,9 @@ import type { Guild } from "discord.js"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import guide from "../reflex/guide.js"; export const event = "guildCreate"; -export async function callback(_client: HaikuClient, guild: Guild) { +export async function callback(_client: NucleusClient, guild: Guild) { guide(guild); } diff --git a/src/events/guildMemberUpdate.ts b/src/events/guildMemberUpdate.ts index 543d6ff..0ebfc3d 100644 --- a/src/events/guildMemberUpdate.ts +++ b/src/events/guildMemberUpdate.ts @@ -1,9 +1,9 @@ import type { GuildAuditLogsEntry, GuildMember } from "discord.js"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; export const event = "guildMemberUpdate"; -export async function callback(client: HaikuClient, before: GuildMember, after: GuildMember) { +export async function callback(client: NucleusClient, before: GuildMember, after: GuildMember) { try { const { log, NucleusColors, entry, renderUser, renderDelta, getAuditLog } = client.logger; const auditLog = await getAuditLog(after.guild, "MEMBER_UPDATE"); diff --git a/src/events/guildUpdate.ts b/src/events/guildUpdate.ts index 703b2d9..e463007 100644 --- a/src/events/guildUpdate.ts +++ b/src/events/guildUpdate.ts @@ -1,10 +1,10 @@ -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import type { Guild, GuildAuditLogsEntry } from "discord.js"; import { callback as statsChannelUpdate } from "../reflex/statsChannelUpdate.js"; export const event = "guildUpdate"; -export async function callback(client: HaikuClient, before: Guild, after: Guild) { +export async function callback(client: NucleusClient, before: Guild, after: Guild) { await statsChannelUpdate(client, after.me!); const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta } = client.logger; const auditLog = await getAuditLog(after, "GUILD_UPDATE"); diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts index 4feee82..ddc143b 100644 --- a/src/events/interactionCreate.ts +++ b/src/events/interactionCreate.ts @@ -6,7 +6,7 @@ import createTranscript from "../premium/createTranscript.js"; import Fuse from "fuse.js"; import { autocomplete as tagAutocomplete } from "../commands/tag.js"; import type { AutocompleteInteraction, Interaction, MessageComponentInteraction } from "discord.js"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; export const event = "interactionCreate"; @@ -102,6 +102,6 @@ async function interactionCreate(interaction: Interaction) { } } -export async function callback(_client: HaikuClient, interaction: Interaction) { +export async function callback(_client: NucleusClient, interaction: Interaction) { await interactionCreate(interaction); } diff --git a/src/events/inviteCreate.ts b/src/events/inviteCreate.ts index 76dfc1e..7dbf8a7 100644 --- a/src/events/inviteCreate.ts +++ b/src/events/inviteCreate.ts @@ -1,11 +1,11 @@ import type { GuildAuditLogsEntry, Invite } from "discord.js"; // @ts-expect-error import humanizeDuration from "humanize-duration"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; export const event = "inviteCreate"; -export async function callback(client: HaikuClient, invite: Invite) { +export async function callback(client: NucleusClient, invite: Invite) { const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderChannel } = client.logger; const auditLog = await getAuditLog(invite.guild, "INVITE_CREATE"); const audit = auditLog.entries diff --git a/src/events/inviteDelete.ts b/src/events/inviteDelete.ts index cdc6828..e74ad15 100644 --- a/src/events/inviteDelete.ts +++ b/src/events/inviteDelete.ts @@ -1,11 +1,11 @@ import type { GuildAuditLogsEntry, Invite } from "discord.js"; // @ts-expect-error import humanizeDuration from "humanize-duration"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; export const event = "inviteDelete"; -export async function callback(client: HaikuClient, invite: Invite) { +export async function callback(client: NucleusClient, invite: Invite) { const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderChannel } = client.logger; const auditLog = await getAuditLog(invite.guild, "INVITE_DELETE"); const audit = auditLog.entries diff --git a/src/events/memberJoin.ts b/src/events/memberJoin.ts index 95dfde2..93c9186 100644 --- a/src/events/memberJoin.ts +++ b/src/events/memberJoin.ts @@ -1,11 +1,11 @@ import type { GuildMember } from "discord.js"; import { callback as statsChannelAdd } from "../reflex/statsChannelUpdate.js"; import { callback as welcome } from "../reflex/welcome.js"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; export const event = "guildMemberAdd"; -export async function callback(client: HaikuClient, member: GuildMember) { +export async function callback(client: NucleusClient, member: GuildMember) { welcome(client, member); statsChannelAdd(client, member); const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger; diff --git a/src/events/memberLeave.ts b/src/events/memberLeave.ts index 8fa0c76..4731d58 100644 --- a/src/events/memberLeave.ts +++ b/src/events/memberLeave.ts @@ -1,12 +1,12 @@ import type { GuildAuditLogsEntry, GuildMember } from "discord.js"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import { purgeByUser } from "../actions/tickets/delete.js"; import { callback as statsChannelRemove } from "../reflex/statsChannelUpdate.js"; export const event = "guildMemberRemove"; -export async function callback(client: HaikuClient, member: GuildMember) { +export async function callback(client: NucleusClient, member: GuildMember) { purgeByUser(member.id, member.guild); await statsChannelRemove(client, member); const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta } = client.logger; diff --git a/src/events/messageCreate.ts b/src/events/messageCreate.ts index 76b6855..3f85de6 100644 --- a/src/events/messageCreate.ts +++ b/src/events/messageCreate.ts @@ -1,4 +1,4 @@ -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import { LinkCheck, MalwareCheck, NSFWCheck, SizeCheck, TestString, TestImage } from "../reflex/scanners.js"; import logAttachment from "../premium/attachmentLogs.js"; import createLogException from "../utils/createLogException.js"; @@ -9,7 +9,7 @@ import { Message, ThreadChannel } from "discord.js"; export const event = "messageCreate"; -export async function callback(_client: HaikuClient, message: Message) { +export async function callback(_client: NucleusClient, message: Message) { if (!message.guild) return; if (message.author.bot) return; if (message.channel.type === "DM") return; diff --git a/src/events/messageDelete.ts b/src/events/messageDelete.ts index bc668f1..ddeff96 100644 --- a/src/events/messageDelete.ts +++ b/src/events/messageDelete.ts @@ -1,9 +1,9 @@ -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import type { GuildAuditLogsEntry, Message } from "discord.js"; export const event = "messageDelete"; -export async function callback(client: HaikuClient, message: Message) { +export async function callback(client: NucleusClient, message: Message) { try { if (message.author.id === client.user.id) return; if (client.noLog.includes(`${message.id}/${message.channel.id}/${message.id}`)) return; diff --git a/src/events/messageEdit.ts b/src/events/messageEdit.ts index 4c482cc..37178a7 100644 --- a/src/events/messageEdit.ts +++ b/src/events/messageEdit.ts @@ -1,9 +1,9 @@ -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import type { Message, MessageReference } from "discord.js"; export const event = "messageUpdate"; -export async function callback(client: HaikuClient, oldMessage: Message, newMessage: Message) { +export async function callback(client: NucleusClient, oldMessage: Message, newMessage: Message) { if (newMessage.author.id === client.user.id) return; if (!newMessage.guild) return; const { log, NucleusColors, entry, renderUser, renderDelta, renderNumberDelta, renderChannel } = client.logger; diff --git a/src/events/roleCreate.ts b/src/events/roleCreate.ts index c8a32b1..25e9c98 100644 --- a/src/events/roleCreate.ts +++ b/src/events/roleCreate.ts @@ -1,9 +1,9 @@ -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import type { GuildAuditLogsEntry, Role } from "discord.js"; export const event = "roleCreate"; -export async function callback(client: HaikuClient, role: Role) { +export async function callback(client: NucleusClient, role: Role) { const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderRole } = client.logger; if (role.managed) return; const auditLog = await getAuditLog(role.guild, "ROLE_CREATE"); diff --git a/src/events/roleDelete.ts b/src/events/roleDelete.ts index 1f71f25..4c89565 100644 --- a/src/events/roleDelete.ts +++ b/src/events/roleDelete.ts @@ -1,10 +1,10 @@ import getEmojiByName from "../utils/getEmojiByName.js"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import type { GuildAuditLogsEntry, Role } from "discord.js"; export const event = "roleDelete"; -export async function callback(client: HaikuClient, role: Role) { +export async function callback(client: NucleusClient, role: Role) { const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta } = client.logger; if (role.managed) return; const auditLog = await getAuditLog(role.guild, "ROLE_DELETE"); diff --git a/src/events/roleUpdate.ts b/src/events/roleUpdate.ts index 357a22f..fc26d6c 100644 --- a/src/events/roleUpdate.ts +++ b/src/events/roleUpdate.ts @@ -1,10 +1,10 @@ import type { Role } from "discord.js"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import getEmojiByName from "../utils/getEmojiByName.js"; export const event = "roleUpdate"; -export async function callback(client: HaikuClient, or: Role, nr: Role) { +export async function callback(client: NucleusClient, or: Role, nr: Role) { const { getAuditLog, log, NucleusColors, entry, renderDelta, renderUser, renderRole } = client.logger; const auditLog = await getAuditLog(nr.guild, "ROLE_UPDATE"); diff --git a/src/events/stickerCreate.ts b/src/events/stickerCreate.ts index b7a417c..295d48b 100644 --- a/src/events/stickerCreate.ts +++ b/src/events/stickerCreate.ts @@ -1,9 +1,9 @@ -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import type { GuildAuditLogsEntry, Sticker } from "discord.js"; export const event = "stickerDelete"; -export async function callback(client: HaikuClient, emoji: Sticker) { +export async function callback(client: NucleusClient, emoji: Sticker) { const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta } = client.logger; const auditLog = await getAuditLog(emoji.guild, "STICKER_CREATE"); const audit = auditLog.entries.filter((entry: GuildAuditLogsEntry) => entry.target!.id === emoji.id).first(); diff --git a/src/events/stickerDelete.ts b/src/events/stickerDelete.ts index 8130d8a..fed99e6 100644 --- a/src/events/stickerDelete.ts +++ b/src/events/stickerDelete.ts @@ -1,9 +1,9 @@ -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import type { GuildAuditLogsEntry, Sticker } from "discord.js"; export const event = "stickerDelete"; -export async function callback(client: HaikuClient, emoji: Sticker) { +export async function callback(client: NucleusClient, emoji: Sticker) { const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta } = client.logger; const auditLog = await getAuditLog(emoji.guild, "STICKER_DELETE"); const audit = auditLog.entries.filter((entry: GuildAuditLogsEntry) => entry.target!.id === emoji.id).first(); diff --git a/src/events/stickerUpdate.ts b/src/events/stickerUpdate.ts index 1421ec2..0ac000e 100644 --- a/src/events/stickerUpdate.ts +++ b/src/events/stickerUpdate.ts @@ -1,9 +1,9 @@ -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import type { Sticker } from "discord.js"; export const event = "stickerUpdate"; -export async function callback(client: HaikuClient, oe: Sticker, ne: Sticker) { +export async function callback(client: NucleusClient, oe: Sticker, ne: Sticker) { const { getAuditLog, log, NucleusColors, entry, renderDelta, renderUser } = client.logger; if (oe.name === ne.name) return; diff --git a/src/events/threadCreate.ts b/src/events/threadCreate.ts index f0f32e5..1f1b758 100644 --- a/src/events/threadCreate.ts +++ b/src/events/threadCreate.ts @@ -1,10 +1,10 @@ import type { GuildAuditLogsEntry, ThreadChannel } from "discord.js"; // @ts-expect-error import humanizeDuration from "humanize-duration"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; export const event = "threadCreate"; -export async function callback(client: HaikuClient, thread: ThreadChannel) { +export async function callback(client: NucleusClient, thread: ThreadChannel) { const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderChannel } = client.logger; const auditLog = await getAuditLog(thread.guild, "THREAD_CREATE"); const audit = auditLog.entries.filter((entry: GuildAuditLogsEntry) => entry.target!.id === thread.id).first(); diff --git a/src/events/threadDelete.ts b/src/events/threadDelete.ts index c166f86..cdc89ee 100644 --- a/src/events/threadDelete.ts +++ b/src/events/threadDelete.ts @@ -1,10 +1,10 @@ import type { GuildAuditLogsEntry, ThreadChannel } from "discord.js"; // @ts-expect-error import humanizeDuration from "humanize-duration"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; export const event = "threadDelete"; -export async function callback(client: HaikuClient, thread: ThreadChannel) { +export async function callback(client: NucleusClient, thread: ThreadChannel) { const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderChannel } = client.logger; const auditLog = await getAuditLog(thread.guild, "THREAD_UPDATE"); const audit = auditLog.entries.filter((entry: GuildAuditLogsEntry) => entry.target!.id === thread.id).first(); diff --git a/src/events/threadUpdate.ts b/src/events/threadUpdate.ts index 20247d6..9125fcb 100644 --- a/src/events/threadUpdate.ts +++ b/src/events/threadUpdate.ts @@ -1,11 +1,11 @@ import type { GuildAuditLogsEntry, ThreadChannel } from "discord.js"; // @ts-expect-error import humanizeDuration from "humanize-duration"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; export const event = "threadUpdate"; -export async function callback(client: HaikuClient, before: ThreadChannel, after: ThreadChannel) { +export async function callback(client: NucleusClient, before: ThreadChannel, after: ThreadChannel) { const { getAuditLog, log, NucleusColors, entry, renderUser, renderDelta, renderChannel } = client.logger; const auditLog = await getAuditLog(after.guild, "THREAD_UPDATE"); const audit = auditLog.entries.filter((entry: GuildAuditLogsEntry) => entry.target!.id === after.id).first(); diff --git a/src/events/webhookUpdate.ts b/src/events/webhookUpdate.ts index e4e9659..a991c66 100644 --- a/src/events/webhookUpdate.ts +++ b/src/events/webhookUpdate.ts @@ -1,9 +1,9 @@ import type { GuildAuditLogsEntry, Webhook } from "discord.js"; import type Discord from "discord.js"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; export const event = "webhookUpdate"; -export async function callback(client: HaikuClient, channel: Discord.GuildChannel) { +export async function callback(client: NucleusClient, channel: Discord.GuildChannel) { try { const { getAuditLog, log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger; let auditLogCreate = getAuditLog(channel.guild, "WEBHOOK_CREATE"); diff --git a/src/premium/createTranscript.ts b/src/premium/createTranscript.ts index f2358d8..6805017 100644 --- a/src/premium/createTranscript.ts +++ b/src/premium/createTranscript.ts @@ -11,10 +11,9 @@ import { import EmojiEmbed from "../utils/generateEmojiEmbed.js"; import getEmojiByName from "../utils/getEmojiByName.js"; import { PasteClient, Publicity, ExpireDate } from "pastebin-api"; -import config from "../config/main.json" assert { type: "json" }; import client from "../utils/client.js"; -const pbClient = new PasteClient(config.pastebinApiKey); +const pbClient = new PasteClient(client.config.pastebinApiKey); export default async function (interaction: CommandInteraction | MessageComponentInteraction) { if (interaction.channel === null) return; diff --git a/src/reflex/statsChannelUpdate.ts b/src/reflex/statsChannelUpdate.ts index cd1c8ef..d807267 100644 --- a/src/reflex/statsChannelUpdate.ts +++ b/src/reflex/statsChannelUpdate.ts @@ -1,5 +1,5 @@ import type { Guild, User } from "discord.js"; -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import type { GuildMember } from "discord.js"; import convertCurlyBracketString from "../utils/convertCurlyBracketString.js"; import singleNotify from "../utils/singleNotify.js"; @@ -9,7 +9,7 @@ interface PropSchema { name: string; } -export async function callback(client: HaikuClient, member?: GuildMember, guild?: Guild, user?: User) { +export async function callback(client: NucleusClient, member?: GuildMember, guild?: Guild, user?: User) { if (!member && !guild) return; guild = await client.guilds.fetch(member ? member.guild.id : guild!.id); if (!guild) return; diff --git a/src/reflex/verify.ts b/src/reflex/verify.ts index bbb0992..6458439 100644 --- a/src/reflex/verify.ts +++ b/src/reflex/verify.ts @@ -2,11 +2,10 @@ import { LoadingEmbed } from "./../utils/defaultEmbeds.js"; import Discord, { CommandInteraction, GuildMember, - Interaction, MessageComponentInteraction, - Permissions, Role, - ButtonStyle + ButtonStyle, + PermissionsBitField } from "discord.js"; import EmojiEmbed from "../utils/generateEmojiEmbed.js"; import fetch from "node-fetch"; @@ -22,7 +21,7 @@ export interface VerifySchema { uName: string; gName: string; gIcon: string; - interaction: Interaction; + interaction: Discord.MessageComponentInteraction; } function step(i: number) { @@ -44,7 +43,7 @@ export default async function (interaction: CommandInteraction | MessageComponen .setTitle("Verify") .setDescription("Verify is not enabled on this server") .setFooter({ - text: (interaction.member!.permissions as Permissions).has("MANAGE_GUILD") + text: (interaction.member!.permissions as PermissionsBitField).has("ManageGuild") ? "You can enable it by running /settings verify" : "" }) @@ -95,7 +94,7 @@ export default async function (interaction: CommandInteraction | MessageComponen .setEmoji("CONTROL.BLOCKCROSS") ], components: [ - new Discord.ActionRowBuilder().addComponents([ + new Discord.ActionRowBuilder().addComponents([ new Discord.ButtonBuilder() .setLabel("Check webpage") .setStyle(ButtonStyle.Link) @@ -120,9 +119,7 @@ export default async function (interaction: CommandInteraction | MessageComponen }); if ( await NSFWCheck( - (interaction.member as GuildMember).user.displayAvatarURL({ - format: "png" - }) + (interaction.member as GuildMember).user.displayAvatarURL({extension: "png", forceStatic: true}) ) ) { return await interaction.editReply({ @@ -139,7 +136,7 @@ export default async function (interaction: CommandInteraction | MessageComponen }); } } - if (config.filters.wordFilter) { + if (config.filters.wordFilter.enabled) { await interaction.editReply({ embeds: [ new EmojiEmbed() @@ -217,8 +214,8 @@ export default async function (interaction: CommandInteraction | MessageComponen rName: role.name, uName: interaction.member!.user.username, gName: interaction.guild!.name, - gIcon: interaction.guild!.iconURL({ format: "png" }), - interaction: interaction + gIcon: interaction.guild!.iconURL({ extension: "png", size: 256 }) ?? "https://assets-global.website-files.com/6257adef93867e50d84d30e2/636e0a6a49cf127bf92de1e2_icon_clyde_blurple_RGB.png", + interaction: interaction as MessageComponentInteraction }; await interaction.editReply({ embeds: [ @@ -229,7 +226,7 @@ export default async function (interaction: CommandInteraction | MessageComponen .setEmoji("MEMBER.JOIN") ], components: [ - new Discord.ActionRowBuilder().addComponents([ + new Discord.ActionRowBuilder().addComponents([ new Discord.ButtonBuilder() .setLabel("Verify") .setStyle(ButtonStyle.Link) diff --git a/src/reflex/welcome.ts b/src/reflex/welcome.ts index 241185d..47ed140 100644 --- a/src/reflex/welcome.ts +++ b/src/reflex/welcome.ts @@ -1,10 +1,10 @@ -import type { HaikuClient } from "../utils/haiku/index.js"; +import type { NucleusClient } from "../utils/client.js"; import convertCurlyBracketString from "../utils/convertCurlyBracketString.js"; import client from "../utils/client.js"; import EmojiEmbed from "../utils/generateEmojiEmbed.js"; import { GuildChannel, GuildMember, BaseGuildTextChannel } from "discord.js"; -export async function callback(_client: HaikuClient, member: GuildMember) { +export async function callback(_client: NucleusClient, member: GuildMember) { if (member.user.bot) return; const config = await client.database.guilds.read(member.guild.id); if (!config.welcome.enabled) return; diff --git a/src/utils/client.ts b/src/utils/client.ts index 7d608c7..6aa9b43 100644 --- a/src/utils/client.ts +++ b/src/utils/client.ts @@ -1,14 +1,16 @@ -import { Client } from 'discord.js'; +import Discord, { Client, Interaction } from 'discord.js'; import { Logger } from "../utils/log.js"; import Memory from "../utils/memory.js"; import type { VerifySchema } from "../reflex/verify.js"; import { Guilds, History, ModNotes, Premium } from "../utils/database.js"; import EventScheduler from "../utils/eventScheduler.js"; import type { RoleMenuSchema } from "../actions/roleMenu.js"; +import config from "../config/main.json" assert { type: "json" }; class NucleusClient extends Client { logger = Logger; + config: typeof config = config; verify: Record = {}; roleMenu: Record = {}; memory: Memory = new Memory() as Memory; @@ -20,6 +22,14 @@ class NucleusClient extends Client { premium: Premium; eventScheduler: EventScheduler; }; + // commands: Record Discord.SlashCommandBuilder) | + // Discord.SlashCommandSubcommandBuilder | ((builder: Discord.SlashCommandSubcommandBuilder) => Discord.SlashCommandSubcommandBuilder) | Discord.SlashCommandSubcommandGroupBuilder | ((builder: Discord.SlashCommandSubcommandGroupBuilder) => Discord.SlashCommandSubcommandGroupBuilder), + // callback: (interaction: Interaction) => Promise, + // check: (interaction: Interaction) => Promise | boolean + // }> = {}; + commands: Discord.Collection = new Discord.Collection(); constructor(database: typeof NucleusClient.prototype.database) { super({ intents: 32767 }); diff --git a/src/utils/commandRegistration/getFilesInFolder.ts b/src/utils/commandRegistration/getFilesInFolder.ts index a669065..d8a1298 100644 --- a/src/utils/commandRegistration/getFilesInFolder.ts +++ b/src/utils/commandRegistration/getFilesInFolder.ts @@ -1,23 +1,27 @@ import fs from "fs"; -export default async function getSubcommandsInFolder(path: string) { +export default async function getSubcommandsInFolder(path: string, indent: string = "") { const files = fs.readdirSync(path, { withFileTypes: true }).filter( file => !file.name.endsWith(".ts") && !file.name.endsWith(".map") ); const subcommands = []; const subcommandGroups = []; + let errors = 0; for (const file of files) { if (file.name === "_meta.js") continue; - // If its a folder - if (file.isDirectory()) { - // Get the _meta.ts file - console.log(`│ ├─ Loading subcommand group ${file.name}}`) - subcommandGroups.push((await import(`../../../${path}/${file.name}/_meta.js`)).command); - } else if (file.name.endsWith(".js")) { - // If its a file - console.log(`│ ├─ Loading subcommand ${file.name}}`) - subcommands.push((await import(`../../../${path}/${file.name}`)).command); + try { + if (file.isDirectory()) { + // Get the _meta.ts file + subcommandGroups.push((await import(`../../../${path}/${file.name}/_meta.js`)).command); + } else if (file.name.endsWith(".js")) { + // If its a file + console.log(`│ ${indent}├─ Loading subcommand ${file.name}`) + subcommands.push((await import(`../../../${path}/${file.name}`)).command); + } + } catch (e) { + console.error(`│ ${indent}│ └─ Error loading ${file.name}: ${e}`); + errors++; } } - return {subcommands, subcommandGroups}; + return {subcommands, subcommandGroups, errors}; } diff --git a/src/utils/commandRegistration/register.ts b/src/utils/commandRegistration/register.ts index af0b5fc..a734921 100644 --- a/src/utils/commandRegistration/register.ts +++ b/src/utils/commandRegistration/register.ts @@ -1,9 +1,16 @@ -import { SlashCommandBuilder } from 'discord.js'; +import { Interaction, SlashCommandBuilder } from 'discord.js'; import config from "../../config/main.json" assert { type: "json" }; import client from "../client.js"; import fs from "fs"; +const colours = { + red: "\x1b[31m", + green: "\x1b[32m", + yellow: "\x1b[33m", + none: "\x1b[0m" +} + async function registerCommands() { const developmentMode = config.enableDevelopment; const commands = []; @@ -14,15 +21,18 @@ async function registerCommands() { console.log(`Registering ${files.length} commands`) let i = 0; for (const file of files) { - // Box drawing characters: | └ ─ ┌ ┐ ┘ ┬ ┤ ├ ┴ ┼ - console.log(`├─ ${file.name}`) + const last = i === files.length - 1 ? "└" : "├"; if (file.isDirectory()) { + console.log(`${last}─ ${colours.yellow}Loading subcommands of ${file.name}${colours.none}`) commands.push((await import(`../../../${config.commandsFolder}/${file.name}/_meta.js`)).command); } else if (file.name.endsWith(".js")) { - commands.push((await import(`../../../${config.commandsFolder}/${file.name}`)).command); + console.log(`${last}─ ${colours.yellow}Loading command ${file.name}${colours.none}`) + const fetched = (await import(`../../../${config.commandsFolder}/${file.name}`)); + commands.push(fetched.command); + client.commands.set(fetched.command.name, [fetched.check, fetched.callback]); } i++; - console.log(`├─ Loaded ${file.name} [${i} / ${files.length}]`) + console.log(`${last.replace("└", " ").replace("├", "│")} └─ ${colours.green}Loaded ${file.name} [${i} / ${files.length}]${colours.none}`) } console.log(`Loaded ${commands.length} commands, processing...`) const processed = [] @@ -39,11 +49,9 @@ async function registerCommands() { if (developmentMode) { const guild = await client.guilds.fetch(config.developmentGuildID); - guild.commands.set([]) guild.commands.set(processed); console.log(`Commands registered in ${guild.name}`) } else { - client.application!.commands.set([]) client.application!.commands.set(processed); console.log(`Commands registered globally`) } @@ -51,11 +59,83 @@ async function registerCommands() { }; async function registerEvents() { - // pass + console.log("Reading events") + const files = fs.readdirSync(config.eventsFolder, { withFileTypes: true }).filter( + file => !file.name.endsWith(".ts") && !file.name.endsWith(".map") + ); + console.log(`Registering ${files.length} events`) + let i = 0; + let errors = 0; + for (const file of files) { + const last = i === files.length - 1 ? "└" : "├"; + i++; + try { + console.log(`${last}─ ${colours.yellow}Loading event ${file.name}${colours.none}`) + const event = (await import(`../../../${config.eventsFolder}/${file.name}`)); + + client.on(event.event, event.callback.bind(null, client)); + + console.log(`${last.replace("└", " ").replace("├", "│")} └─ ${colours.green}Loaded ${file.name} [${i} / ${files.length}]${colours.none}`) + } catch (e) { + errors++; + console.log(`${last.replace("└", " ").replace("├", "│")} └─ ${colours.red}Failed to load ${file.name} [${i} / ${files.length}]${colours.none}`) + } + } + console.log(`Loaded ${files.length - errors} events (${errors} failed)`) }; +async function registerCommandHandler() { + client.on("interactionCreate", async (interaction: Interaction) => { + if (!interaction.isCommand()) return; + + const commandName = interaction.commandName; + const subcommandGroupName = interaction.options.getSubcommandGroup(false); + const subcommandName = interaction.options.getSubcommand(false); + + let fullCommandName = commandName + (subcommandGroupName ? ` ${subcommandGroupName}` : "") + (subcommandName ? ` ${subcommandName}` : ""); + + const command = this.commands.get(fullCommandName); + if (!command) return; + + const sendErrorMessage = async (error: Error) => { + if (this.listenerCount("commandError")) { + return this.emit("commandError", interaction, error); + } + let method = (!interaction.deferred && !interaction.replied) ? interaction.reply.bind(interaction) : interaction.followUp.bind(interaction); + await method({ + embeds: [ + new Embed() + .setColor(0xff0000) + .setTitle("I couldn't run that command") + .setDescription(error.message ?? error.toString()) + ] + , ephemeral: true}); + } + + try { + let hasPermission = await command.check(interaction); + + if (!hasPermission) { + sendErrorMessage(new CheckFailedError("You don't have permission to run this command")); + return; + } + } catch (error) { + sendErrorMessage(error); + return; + } + try { + await command.callback(interaction); + } catch (error) { + this._error(error); + sendErrorMessage(error); + return; + } + }); +} + export default async function register() { - console.log("> Registering commands") await registerCommands(); + await registerCommandHandler(); await registerEvents(); + console.log(`${colours.green}Registered commands and events${colours.none}`) }; diff --git a/src/utils/commandRegistration/slashCommandBuilder.ts b/src/utils/commandRegistration/slashCommandBuilder.ts index 719855f..c7ac55f 100644 --- a/src/utils/commandRegistration/slashCommandBuilder.ts +++ b/src/utils/commandRegistration/slashCommandBuilder.ts @@ -4,8 +4,17 @@ import config from "../../config/main.json" assert { type: "json" }; import getSubcommandsInFolder from "./getFilesInFolder.js"; +const colours = { + red: "\x1b[31m", + green: "\x1b[32m", + none: "\x1b[0m" +} + + export async function group(name: string, description: string, path: string) { - const fetched = await getSubcommandsInFolder(config.commandsFolder + "/" + path) + console.log(`│ ├─ Loading group ${name}`) + const fetched = await getSubcommandsInFolder(config.commandsFolder + "/" + path, "│ ") + console.log(`│ │ └─ ${fetched.errors ? colours.red : colours.green}Loaded ${fetched.subcommands.length} subcommands for ${name} (${fetched.errors} failed)${colours.none}`) return (subcommandGroup: SlashCommandSubcommandGroupBuilder) => { subcommandGroup .setName(name) @@ -21,7 +30,7 @@ export async function group(name: string, description: string, path: string) { export async function command(name: string, description: string, path: string) { const fetched = await getSubcommandsInFolder(config.commandsFolder + "/" + path); - console.log(`│ ├─ Loaded ${fetched.subcommands.length} subcommands and ${fetched.subcommandGroups.length} subcommand groups for ${name}`) + console.log(`│ ├─ ${fetched.errors ? colours.red : colours.green}Loaded ${fetched.subcommands.length} subcommands and ${fetched.subcommandGroups.length} subcommand groups for ${name} (${fetched.errors} failed)${colours.none}`) return (command: SlashCommandBuilder) => { command.setName(name) command.setDescription(description)