UNPKG

seyfert

Version:

The most advanced framework for discord bots

341 lines (340 loc) 14.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseClient = void 0; const node_path_1 = require("node:path"); const api_1 = require("../api"); const cache_1 = require("../cache"); const shared_1 = require("../commands/applications/shared"); const handler_1 = require("../commands/handler"); const common_1 = require("../common"); const node_fs_1 = require("node:fs"); const utils_1 = require("../api/utils/utils"); const handle_1 = require("../commands/handle"); const bans_1 = require("../common/shorters/bans"); const soundboard_1 = require("../common/shorters/soundboard"); const voiceStates_1 = require("../common/shorters/voiceStates"); const handler_2 = require("../components/handler"); const handler_3 = require("../langs/handler"); class BaseClient { rest = new api_1.ApiHandler({ token: 'INVALID' }); cache = new cache_1.Cache(0, new cache_1.MemoryAdapter(), {}, this); applications = new common_1.ApplicationShorter(this); users = new common_1.UsersShorter(this); channels = new common_1.ChannelShorter(this); guilds = new common_1.GuildShorter(this); messages = new common_1.MessageShorter(this); members = new common_1.MemberShorter(this); webhooks = new common_1.WebhookShorter(this); templates = new common_1.TemplateShorter(this); roles = new common_1.RoleShorter(this); reactions = new common_1.ReactionShorter(this); emojis = new common_1.EmojiShorter(this); threads = new common_1.ThreadShorter(this); bans = new bans_1.BanShorter(this); interactions = new common_1.InteractionShorter(this); voiceStates = new voiceStates_1.VoiceStateShorter(this); soundboards = new soundboard_1.SoundboardShorter(this); debugger; logger = new common_1.Logger({ name: '[Seyfert]', }); langs = new handler_3.LangsHandler(this.logger); commands = new handler_1.CommandHandler(this.logger, this); components = new handler_2.ComponentHandler(this.logger, this); handleCommand; _applicationId; _botId; middlewares; static getBotIdFromToken(token) { return Buffer.from(token.split('.')[0], 'base64').toString('ascii'); } options; /**@internal */ static _seyfertCfWorkerConfig; constructor(options) { this.options = (0, common_1.MergeOptions)({ commands: { defaults: { onRunError(context, error) { context.client.logger.fatal(`${context.command.name}.<onRunError>`, context.author.id, error); }, onOptionsError(context, metadata) { context.client.logger.fatal(`${context.command.name}.<onOptionsError>`, context.author.id, metadata); }, onMiddlewaresError(context, error) { context.client.logger.fatal(`${context.command.name}.<onMiddlewaresError>`, context.author.id, error); }, onBotPermissionsFail(context, permissions) { context.client.logger.fatal(`${context.command.name}.<onBotPermissionsFail>`, context.author.id, permissions); }, onPermissionsFail(context, permissions) { context.client.logger.fatal(`${context.command.name}.<onPermissionsFail>`, context.author.id, permissions); }, onInternalError(client, command, error) { client.logger.fatal(`${command.name}.<onInternalError>`, error); }, }, }, components: { defaults: { onRunError(context, error) { context.client.logger.fatal('ComponentCommand.<onRunError>', context.author.id, error); }, onMiddlewaresError(context, error) { context.client.logger.fatal('ComponentCommand.<onMiddlewaresError>', context.author.id, error); }, onInternalError(client, error) { client.logger.fatal(error); }, }, }, modals: { defaults: { onRunError(context, error) { context.client.logger.fatal('ModalCommand.<onRunError>', context.author.id, error); }, onMiddlewaresError(context, error) { context.client.logger.fatal('ModalCommand.<onMiddlewaresError>', context.author.id, error); }, onInternalError(client, error) { client.logger.fatal(error); }, }, }, }, options); } get proxy() { return this.rest.proxy; } set botId(id) { this._botId = id; } get botId() { return this._botId ?? BaseClient.getBotIdFromToken(this.rest.options.token); } set applicationId(id) { this._applicationId = id; } get applicationId() { return this._applicationId ?? this.botId; } setServices({ rest, cache, langs, middlewares, handleCommand }) { if (rest) { rest.onRatelimit ??= this.rest.onRatelimit?.bind(rest); this.rest = rest; } if (cache) { const caches = [ 'bans', 'channels', 'emojis', 'guilds', 'members', 'messages', 'onPacket', 'overwrites', 'presences', 'roles', 'stageInstances', 'stickers', 'users', 'voiceStates', ]; let disabledCache = {}; if (typeof cache.disabledCache === 'boolean') { for (const i of caches) { disabledCache[i] = cache.disabledCache; } } else if (typeof cache.disabledCache === 'function') { for (const i of caches) { disabledCache[i] = cache.disabledCache(i); } } else if (typeof cache.disabledCache === 'object') { disabledCache = cache.disabledCache; } if (cache.adapter) this.cache.adapter = cache.adapter; if (cache.disabledCache) this.cache.buildCache(disabledCache, this); } if (middlewares) { this.middlewares = middlewares; } if (langs) { this.langs ??= new handler_3.LangsHandler(this.logger); if (langs.default) this.langs.defaultLang = langs.default; if (langs.aliases) this.langs.aliases = Object.entries(langs.aliases); } if (handleCommand) this.handleCommand = new handleCommand(this); } async execute(..._options) { if ((await this.getRC()).debug) { this.debugger = new common_1.Logger({ name: '[Debug]', logLevel: common_1.LogLevels.Debug, }); } } async start(options = {}) { await this.loadLangs(options.langsDir); await this.loadCommands(options.commandsDir); await this.loadComponents(options.componentsDir); const { token: tokenRC, debug } = await this.getRC(); const token = options.token ?? tokenRC; (0, common_1.assertString)(token, 'token is not a string'); if (this.rest.options.token === 'INVALID') this.rest.options.token = token; this.rest.debug = debug; if (!this.handleCommand) this.handleCommand = new handle_1.HandleCommand(this); // The reason of this method is so for adapters that need to connect somewhere, have time to connect. // Or maybe clear cache? await this.cache.adapter.start(); } async onPacket(..._packet) { throw new Error('Function not implemented'); } /** * * @param rawBody body of interaction * @returns */ async onInteractionRequest(rawBody) { return new Promise(async (r) => { await this.handleCommand.interaction(rawBody, -1, async ({ body, files }) => { let response; const headers = {}; if (files) { response = new FormData(); for (const [index, file] of files.entries()) { const fileKey = file.key ?? `files[${index}]`; if ((0, utils_1.isBufferLike)(file.data)) { response.append(fileKey, new Blob([file.data], { type: file.contentType }), file.filename); } else { response.append(fileKey, new Blob([`${file.data}`], { type: file.contentType }), file.filename); } } if (body) { response.append('payload_json', JSON.stringify(body)); } } else { response = body ?? {}; headers['Content-Type'] = 'application/json'; } return r({ headers, response, }); }); }); } async shouldUploadCommands(cachePath, guildId) { const should = await this.commands.shouldUpload(cachePath, guildId); this.logger.debug(should ? `[${guildId ?? 'global'}] Change(s) detected, uploading commands` : `[${guildId ?? 'global'}] commands seems to be up to date`); return should; } syncCachePath(cachePath) { return node_fs_1.promises.writeFile(cachePath, JSON.stringify(this.commands.values .filter(cmd => !('ignore' in cmd) || cmd.ignore !== shared_1.IgnoreCommand.Slash) .map(x => x.toJSON()))); } async uploadCommands({ applicationId, cachePath } = {}) { applicationId ??= await this.getRC().then(x => x.applicationId ?? this.applicationId); (0, common_1.assertString)(applicationId, 'applicationId is not a string'); const commands = this.commands.values; const filter = (0, common_1.filterSplit)(commands, command => ('guildId' in command ? !command.guildId : true)); if (this.commands.entryPoint) { filter.expect.push(this.commands.entryPoint); } if (!cachePath || (await this.shouldUploadCommands(cachePath))) await this.proxy.applications(applicationId).commands.put({ body: filter.expect .filter(cmd => !('ignore' in cmd) || cmd.ignore !== shared_1.IgnoreCommand.Slash) .map(x => x.toJSON()), }); const guilds = new Set(); for (const command of filter.never) { for (const guild_id of command.guildId) { guilds.add(guild_id); } } for (const guildId of guilds) { if (!cachePath || (await this.shouldUploadCommands(cachePath, guildId))) { await this.proxy .applications(applicationId) .guilds(guildId) .commands.put({ body: filter.never .filter(cmd => cmd.guildId.includes(guildId) && (!('ignore' in cmd) || cmd.ignore !== shared_1.IgnoreCommand.Slash)) .map(x => x.toJSON()), }); } } if (cachePath) await this.syncCachePath(cachePath); } async loadCommands(dir) { dir ??= await this.getRC().then(x => x.locations.commands); if (dir && this.commands) { await this.commands.load(dir, this); this.logger.info('CommandHandler loaded'); } } async loadComponents(dir) { dir ??= await this.getRC().then(x => x.locations.components); if (dir && this.components) { await this.components.load(dir); this.logger.info('ComponentHandler loaded'); } } async loadLangs(dir) { dir ??= await this.getRC().then(x => x.locations.langs); if (dir && this.langs) { await this.langs.load(dir); this.logger.info('LangsHandler loaded'); } } t(locale) { return this.langs.get(locale); } async getRC() { const seyfertConfig = (BaseClient._seyfertCfWorkerConfig || (await this.options?.getRC?.()) || (await Promise.any(['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts'].map(ext => (0, common_1.magicImport)((0, node_path_1.join)(process.cwd(), `seyfert.config${ext}`)).then(x => x.default ?? x))).catch((e) => { if (e.errors.every((err) => err.stack?.includes('ERR_MODULE_NOT_FOUND'))) { throw new Error('No seyfert.config file found'); } throw e.errors.find((err) => !err.stack?.includes('ERR_MODULE_NOT_FOUND')) ?? e.errors[0]; }))); const { locations, debug, ...env } = seyfertConfig; const locationsFullPaths = { base: locations.base, }; for (const i in locations) { const key = i; const location = locations[key]; if (key in locationsFullPaths) continue; if (typeof location === 'string') locationsFullPaths[key] = (0, node_path_1.join)(process.cwd(), locations.base, location); else locationsFullPaths[key] = location; } const obj = { debug: !!debug, ...env, locations: locationsFullPaths, }; return obj; } } exports.BaseClient = BaseClient;