UNPKG

seyfert

Version:

The most advanced framework for discord bots

394 lines (393 loc) 12.8 kB
"use strict"; 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'); } }