seyfert
Version:
The most advanced framework for discord bots
341 lines (340 loc) • 14.1 kB
JavaScript
"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;