import { PrismaClient } from '@prisma/client'; import { ApplicationCommandDataResolvable, Client, ClientEvents, Collection, GatewayIntentBits, Options, Partials } from 'discord.js' import { glob } from 'glob'; import { promisify } from 'util'; import { CICommandType, MessageCtxCommandType, UserCtxCommandType } from '../typings/Command'; import { eventLogger } from '../utils/logger'; import { Event } from './Event'; const globPromise = promisify(glob); export const db = new PrismaClient(); export class ExtendedClient extends Client { constructor() { super({ intents: [ GatewayIntentBits.DirectMessages, GatewayIntentBits.GuildMembers, GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildInvites ], partials: [ Partials.Channel, Partials.GuildMember, Partials.GuildScheduledEvent, Partials.Message, Partials.Reaction, Partials.User ], allowedMentions: { repliedUser: false, parse: ['roles', 'users'] }, makeCache: Options.cacheWithLimits({ GuildInviteManager: 100, GuildMemberManager: { maxSize: 250, keepOverLimit: (member) => member.id === this.user.id }, MessageManager: { maxSize: 100, keepOverLimit: (message) => message.author.id === this.user.id } }) }); } stickyCache: Collection = new Collection(); chatCommands: Collection = new Collection(); ctxCommands: Collection = new Collection(); devCommands: Collection = new Collection(); cmdCategories: Set = new Set(); logger = eventLogger; async start() { db.$connect(); this.registerModules(); this.login(process.env.CLIENT_TOKEN); } async importFile(filePath: string) { return (await import(filePath))?.default; } async registerGlobalCommands({ commands }: RegisterCommandsOptions) { const cmds = await this.application.commands.fetch(); if(cmds.size !== commands.length) { if(this.shard) this.shard?.broadcastEval(c => { if(c.shard?.ids.includes(0)) c.application.commands.set(commands) }); else this.application.commands.set(commands); } } async registerDevCommands({ commands }: RegisterCommandsOptions) { if(this.guilds.cache.get(process.env.DEV_GUILD)) await this.guilds.cache.get(process.env.DEV_GUILD).commands.set(commands); } async registerModules() { const globalCommands: ApplicationCommandDataResolvable[] = []; const devCommands: ApplicationCommandDataResolvable[] = []; const commandFiles = await Promise.all([ await globPromise(`${__dirname}/../commands/chat/**/*{.ts,.js}`), await globPromise(`${__dirname}/../commands/context/**/*{.ts,.js}`), await globPromise(`${__dirname}/../commands/dev/*{.ts,.js}`) ]); commandFiles[0].forEach(async(filePath) => { const command: CICommandType = await this.importFile(filePath); if(!command.name) return; this.chatCommands.set(command.name, command); this.cmdCategories.add(filePath.split('/').reverse()[1]); globalCommands.push(command); if(process.env.ENVIRONMENT === 'dev') devCommands.push(command); }); commandFiles[1].forEach(async(filePath) => { const command: UserCtxCommandType | MessageCtxCommandType = await this.importFile(filePath); if(!command.name) return; this.ctxCommands.set(command.name, command); this.cmdCategories.add(filePath.split('/').reverse()[1]); globalCommands.push(command); if(process.env.ENVIRONMENT === 'dev') devCommands.push(command); }); commandFiles[2].forEach(async(filePath) => { const command: CICommandType | UserCtxCommandType | MessageCtxCommandType = await this.importFile(filePath); if(!command.name) return; this.devCommands.set(command.name, command); devCommands.push(command); }); this.on('ready', () => { if(process.env.ENVIRONMENT === 'dev') { this.registerDevCommands({ commands: devCommands }); } if(process.env.ENVIRONMENT === 'prod') { this.registerGlobalCommands({ commands: globalCommands }); this.registerDevCommands({ commands: devCommands }); } }); const eventFiles = await globPromise(`${__dirname}/../events/*{.ts,.js}`); eventFiles.forEach(async(filePath) => { const event: Event = await this.importFile(filePath); this.on(event.event, event.run); }); } } interface RegisterCommandsOptions { commands: ApplicationCommandDataResolvable[]; }