seyfert
Version:
The most advanced framework for discord bots
394 lines (393 loc) • 12.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReplaceRegex = exports.BaseHandler = void 0;
exports.calculateShardId = calculateShardId;
exports.resolveColor = resolveColor;
exports.delay = delay;
exports.isObject = isObject;
exports.MergeOptions = MergeOptions;
exports.filterSplit = filterSplit;
exports.toSnakeCase = toSnakeCase;
exports.toCamelCase = toCamelCase;
exports.magicImport = magicImport;
exports.fakePromise = fakePromise;
exports.lazyLoadPackage = lazyLoadPackage;
exports.isCloudfareWorker = isCloudfareWorker;
exports.snowflakeToTimestamp = snowflakeToTimestamp;
exports.resolvePartialEmoji = resolvePartialEmoji;
exports.resolveEmoji = resolveEmoji;
exports.encodeEmoji = encodeEmoji;
exports.hasProps = hasProps;
exports.hasIntent = hasIntent;
exports.toArrayBuffer = toArrayBuffer;
exports.toBuffer = toBuffer;
exports.assertString = assertString;
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const __1 = require("..");
const types_1 = require("../../types");
/**
* Calculates the shard ID for a guild based on its ID.
* @param guildId The ID of the guild.
* @param shards The number of shards to calculate the ID for.
* @returns The shard ID.
*/
function calculateShardId(guildId, shards) {
return Number((BigInt(guildId) >> 22n) % BigInt(shards ?? 1));
}
// It is used for resolve color for better performance
const ColorLookup = {
Random: -1, // Special value for random color
...__1.EmbedColors,
};
/**
* Resolves the color to a numeric representation.
* @param color The color to resolve.
* @returns The numeric representation of the color.
*/
function resolveColor(color) {
const type = typeof color;
if (type === 'number') {
if (!Number.isInteger(color) || color < 0)
throw new Error(`Invalid color: ${color}`);
return color;
}
if (type === 'string') {
const lookupColor = ColorLookup[color];
if (lookupColor) {
return lookupColor === -1 ? Math.floor(Math.random() * 0xffffff) : lookupColor;
}
return color.startsWith('#') ? Number.parseInt(color.slice(1), 16) : __1.EmbedColors.Default;
}
return Array.isArray(color) && color.length >= 3
? (color[0] << 16) | (color[1] << 8) | color[2]
: __1.EmbedColors.Default;
}
/**
* Delays the resolution of a Promise by the specified time.
* @param time The time in milliseconds to delay the resolution.
* @param result The value to resolve with after the delay.
* @returns A Promise that resolves after the specified time with the provided result.
*/
function delay(time, result) {
return new Promise(r => setTimeout(r, time, result));
}
/**
* Checks if a given value is an object.
* @param o The value to check.
* @returns `true` if the value is an object, otherwise `false`.
*/
function isObject(o) {
return o && typeof o === 'object' && !Array.isArray(o);
}
/**
* Merges multiple options objects together, deeply extending objects.
* @param defaults The default options object.
* @param options Additional options objects to merge.
* @returns The merged options object.
*/
function MergeOptions(defaults, ...options) {
const option = options.shift();
if (!option) {
return defaults;
}
return MergeOptions({
...option,
...Object.fromEntries(Object.entries(defaults).map(([key, value]) => [
key,
isObject(value) ? MergeOptions(value, option?.[key] || {}) : (option?.[key] ?? value),
])),
}, ...options);
}
/**
* Splits an array into two arrays based on the result of a predicate function.
* @param arr The array to split.
* @param func The predicate function used to test elements of the array.
* @returns An object containing two arrays: one with elements that passed the test and one with elements that did not.
*/
function filterSplit(arr, func) {
const expect = [];
const never = [];
for (const element of arr) {
const test = func(element);
if (test)
expect.push(element);
else
never.push(element);
}
return { expect, never };
}
/**
* Represents a base handler class.
*/
class BaseHandler {
logger;
/**
* Initializes a new instance of the BaseHandler class.
* @param logger The logger instance.
*/
constructor(logger) {
this.logger = logger;
}
/**
* Filters a file path.
* @param path The path to filter.
* @returns `true` if the path passes the filter, otherwise `false`.
*/
filter = (path) => !!path;
/**
* Recursively retrieves all files in a directory.
* @param dir The directory path.
* @returns A Promise that resolves to an array of file paths.
*/
async getFiles(dir) {
const files = [];
for (const i of await node_fs_1.promises.readdir(dir, { withFileTypes: true })) {
if (i.isDirectory()) {
files.push(...(await this.getFiles((0, node_path_1.join)(dir, i.name))));
}
else if (this.filter((0, node_path_1.join)(dir, i.name))) {
files.push((0, node_path_1.join)(dir, i.name));
}
}
return files;
}
/**
* Loads files from given paths.
* @param paths The paths of the files to load.
* @returns A Promise that resolves to an array of loaded files.
*/
loadFiles(paths) {
return Promise.all(paths.map(path => magicImport(path).then(file => file.default ?? file)));
}
/**
* Loads files from given paths along with additional information.
* @param paths The paths of the files to load.
* @returns A Promise that resolves to an array of objects containing name, file, and path.
*/
loadFilesK(paths) {
return Promise.all(paths.map(path => magicImport(path).then(file => {
return {
name: (0, node_path_1.basename)(path),
file,
path,
};
})));
}
}
exports.BaseHandler = BaseHandler;
/**
* Convert a camelCase object to snake_case.
* @param target The object to convert.
* @returns The converted object.
*/
function toSnakeCase(target) {
const result = {};
for (const [key, value] of Object.entries(target)) {
switch (typeof value) {
case 'string':
case 'bigint':
case 'boolean':
case 'function':
case 'number':
case 'symbol':
case 'undefined':
result[exports.ReplaceRegex.snake(key)] = value;
break;
case 'object': {
if (Array.isArray(value)) {
result[exports.ReplaceRegex.snake(key)] = value.map(prop => typeof prop === 'object' && prop ? toSnakeCase(prop) : prop);
break;
}
if (isObject(value)) {
result[exports.ReplaceRegex.snake(key)] = toSnakeCase(value);
break;
}
if (!Number.isNaN(value)) {
result[exports.ReplaceRegex.snake(key)] = null;
break;
}
result[exports.ReplaceRegex.snake(key)] = toSnakeCase(value);
break;
}
}
}
return result;
}
/**
* Convert a snake_case object to camelCase.
* @param target The object to convert.
* @returns The converted object.
*/
function toCamelCase(target) {
const result = {};
for (const [key, value] of Object.entries(target)) {
switch (typeof value) {
case 'string':
case 'bigint':
case 'boolean':
case 'function':
case 'symbol':
case 'number':
case 'undefined':
result[exports.ReplaceRegex.camel(key)] = value;
break;
case 'object': {
if (Array.isArray(value)) {
result[exports.ReplaceRegex.camel(key)] = value.map(prop => typeof prop === 'object' && prop ? toCamelCase(prop) : prop);
break;
}
if (isObject(value)) {
result[exports.ReplaceRegex.camel(key)] = toCamelCase(value);
break;
}
if (!Number.isNaN(value)) {
result[exports.ReplaceRegex.camel(key)] = null;
break;
}
result[exports.ReplaceRegex.camel(key)] = toCamelCase(value);
break;
}
}
}
return result;
}
exports.ReplaceRegex = {
camel: (s) => {
return s.toLowerCase().replace(/(_\S)/gi, a => a[1].toUpperCase());
},
snake: (s) => {
return s.replace(/[A-Z]/g, a => `_${a.toLowerCase()}`);
},
};
async function magicImport(path) {
try {
if ('Deno' in globalThis)
throw new Error('https://github.com/denoland/deno/issues/26136');
return require(path);
}
catch (e) {
// (bun)dows moment
if (!('Bun' in globalThis) || e.message.includes('is unsupported. use "await import()" instead.'))
return new Function('path', 'return import(`file:///${path}?update=${Date.now()}`)')(path.split('\\').join('\\\\'));
throw new Error(`Cannot import ${path}`, { cause: e });
}
}
function fakePromise(value) {
if (value instanceof Promise)
return value;
return {
then: callback => callback(value),
};
}
function lazyLoadPackage(mod) {
try {
return require(mod);
}
catch (e) {
// biome-ignore lint/suspicious/noConsoleLog:
// biome-ignore lint/suspicious/noConsole:
console.log(`Cannot import ${mod}`, e);
return;
}
}
function isCloudfareWorker() {
//@ts-expect-error
return process.platform === 'browser';
}
/**
*
* Convert a timestamp to a snowflake.
* @param id The timestamp to convert.
* @returns The snowflake.
*/
function snowflakeToTimestamp(id) {
return (BigInt(id) >> 22n) + __1.DiscordEpoch;
}
function resolvePartialEmoji(emoji) {
if (typeof emoji === 'string') {
const groups = emoji.match(types_1.FormattingPatterns.Emoji)?.groups;
if (groups) {
return {
animated: !!groups.animated,
name: groups.name,
id: groups.id,
};
}
if (emoji.includes('%')) {
emoji = encodeURIComponent(emoji);
}
if (!(emoji.includes(':') || emoji.match(/\d{17,20}/g))) {
return { name: emoji, id: null };
}
return;
}
if (!(emoji.id && emoji.name))
return;
return { id: emoji.id, name: emoji.name, animated: !!emoji.animated };
}
async function resolveEmoji(emoji, cache) {
const partial = resolvePartialEmoji(emoji);
if (partial)
return partial;
if (typeof emoji === 'string') {
if (!emoji.match(/\d{17,20}/g))
return;
const fromCache = await cache.emojis?.get(emoji);
return (fromCache && {
animated: fromCache.animated,
id: fromCache.id,
name: fromCache.name,
});
}
if (emoji.id) {
const fromCache = await cache.emojis?.get(emoji.id);
if (fromCache)
return {
animated: fromCache.animated,
id: fromCache.id,
name: fromCache.name,
};
}
return;
}
function encodeEmoji(rawEmoji) {
return rawEmoji.id ? `${rawEmoji.name}:${rawEmoji.id}` : `${rawEmoji.name}`;
}
function hasProps(target, props) {
if (Array.isArray(props)) {
return props.every(x => hasProps(target, x));
}
if (!(props in target)) {
return false;
}
if (typeof target[props] === 'string' && !target[props].length) {
return false;
}
return true;
}
function hasIntent(intents, target) {
const intent = typeof target === 'string' ? types_1.GatewayIntentBits[target] : target;
return (intents & intent) === intent;
}
function toArrayBuffer(buffer) {
const arrayBuffer = new ArrayBuffer(buffer.length);
const view = new Uint8Array(arrayBuffer);
for (let i = 0; i < buffer.length; ++i) {
view[i] = buffer[i];
}
return arrayBuffer;
}
function toBuffer(arrayBuffer) {
const buffer = Buffer.alloc(arrayBuffer.byteLength);
const view = new Uint8Array(arrayBuffer);
for (let i = 0; i < buffer.length; ++i) {
buffer[i] = view[i];
}
return buffer;
}
function assertString(value, message) {
if (!(typeof value === 'string' && value !== '')) {
throw new Error(message ?? 'Value is not a string');
}
}