/* eslint-disable max-len */
/* Requires */
const axios = require('axios');
const sharp = require('sharp');
const fs = require('fs');
const Indexer = require('../../index');
/* JSON's | Utilidades */
const envInfo = JSON.parse(fs.readFileSync(`${__dirname}/utils.json`));
const isNumeric = /^-?[0-9]+$/;
/**
* Retorna todos os detalhes do ambiente (`envInfo`).
*
* @returns {Object} O objeto `envInfo`, que contém os detalhes do ambiente da execução.
*/
function ambientDetails() {
/* Retorna a envData */
return envInfo;
}
/**
* Realiza um merge profundo (deep merge) entre dois objetos, combinando propriedades recursivamente.
*
* @param {Object} target - O objeto destino que receberá as propriedades mescladas.
* @param {Object} source - O objeto fonte de onde as propriedades serão copiadas.
* @returns {Object} O objeto target modificado com as propriedades mescladas.
*
* @example
* const obj1 = { a: 1, b: { c: 2 } };
* const obj2 = { b: { d: 3 }, e: 4 };
* deepMerge(obj1, obj2);
* // Resultado: { a: 1, b: { c: 2, d: 3 }, e: 4 }
*
* @description
* Esta função percorre todas as propriedades do objeto source e:
* 1. Se a propriedade é um objeto (não array) em ambos target e source, faz merge recursivo
* 2. Caso contrário, sobrescreve a propriedade no target com o valor do source
*
* Observação: Arrays e objetos especiais (como Date, RegExp) serão sobrescritos, não mesclados.
*/
function deepMerge(target, source) {
/* Reseta o sucesso */
envInfo.results.success = false;
/* Define o valor padrão */
envInfo.results.value = {
date: true,
};
/* Try-Catch para casos de erro */
try {
/* Cria uma cópia para evitar mutação direta do parâmetro */
const result = { ...target };
/* Inicia a iteração com os dados */
Object.keys(source).forEach((key) => {
/* Define ambos */
const sourceValue = source[key];
const targetValue = target[key];
/* Se ambos são objetos e não são arrays, faz merge recursivo */
if (sourceValue instanceof Object
&& !Array.isArray(sourceValue)
&& targetValue instanceof Object
&& !Array.isArray(targetValue)) {
result[key] = deepMerge(targetValue, sourceValue);
/* Caso contrário, simplesmente atribui o valor */
} else result[key] = sourceValue;
});
/* Define o resultado e sucesso */
envInfo.results.value = result;
envInfo.results.success = true;
/* Caso de algum erro */
} catch (error) {
/* Insere tudo na envInfo */
logging.echoError(error, envInfo, __dirname);
}
/* Retorna os resultados */
return logging.postResults(envInfo);
}
/**
* Converte e valida uma data (string ou timestamp) em um objeto padronizado.
* @param {string|number} dateInput - Data como string (dd/mm/yy) ou timestamp.
* @returns {Object} - Resultado com status, timestamp e formatos de data.
*/
function isValidDate(dateInput) {
/* Inicializa o objeto de resultados */
const result = {
success: false,
value: {
date: false,
toUnix: 0,
formatted: {},
},
};
/* Bloco try-catch para tratamento de erros */
try {
/* Declara variável para o objeto Date */
let dateObj;
/* Declara flag de validação */
let isValid = false;
/* Verifica se o input é um número (timestamp) */
if (typeof dateInput === 'number') {
/* Cria objeto Date diretamente do timestamp */
dateObj = new Date(dateInput);
/* Verifica se a data é válida */
isValid = !Number.isNaN(dateObj.getTime());
/* Verifica se o input é uma string (data formatada) */
} else if (typeof dateInput === 'string') {
/* Executa o teste de padrão na string */
const match = dateInput.match(/^(\d{2})([/-])(\d{2})\2(\d{2,4})$/);
/* Se o padrão for encontrado */
if (match) {
/* Extrai dia, separador, mês e ano da string */
const [, day, , month, yearStr] = match;
/* Converte para números */
const dayNum = parseInt(day, 10);
const monthNum = parseInt(month, 10) - 1; // Mês é 0-indexed no JS
let yearNum = parseInt(yearStr, 10);
/* Corrige anos com 2 dígitos (assume século 21 para 00-99) */
if (yearStr.length === 2) {
yearNum = 2000 + yearNum;
}
/* Cria objeto Date usando o construtor com parâmetros separados */
dateObj = new Date(yearNum, monthNum, dayNum);
/* Verifica se a data é válida e corresponde aos valores de entrada */
isValid = (!Number.isNaN(dateObj.getTime())
&& dateObj.getDate() === dayNum
&& dateObj.getMonth() === monthNum
&& dateObj.getFullYear() === yearNum
);
}
/* Se não for data válida ou não match no padrão */
if (!isValid) {
/* Usa data atual como fallback */
dateObj = new Date();
}
}
/* Se não for string nem número válido */
if (!dateObj) {
/* Cria objeto Date com data atual */
dateObj = new Date();
}
/* Preenche os valores de resultado */
result.value = {
date: isValid,
toUnix: Math.floor(dateObj.getTime() / 1000), /* Unix */
toISOString: dateObj.toISOString(), /* Formato ISO */
toUTCString: dateObj.toUTCString(), /* Formato UTC */
toLocaleString: dateObj.toLocaleString(), /* Formato local */
toDateString: dateObj.toDateString(), /* Data legível */
toTimeString: dateObj.toTimeString(), /* Hora legível */
toLocaleDateString: dateObj.toLocaleDateString(), /* Data local */
toLocaleTimeString: dateObj.toLocaleTimeString(), /* Hora local */
};
/* Define o status de sucesso */
result.success = isValid;
/* Captura erros durante o processamento */
} catch (error) {
/* Loga o erro no console */
console.error('Erro ao processar data:', error);
/* Cria objeto Date atual para fallback */
const now = new Date();
/* Preenche os valores de resultado com fallback */
result.value = {
date: false,
toUnix: Date.now(), /* Timestamp atual */
toISOString: now.toISOString(), /* Formato ISO */
toUTCString: now.toUTCString(), /* Formato UTC */
toLocaleString: now.toLocaleString(), /* Formato local */
toDateString: now.toDateString(), /* Data legível */
toTimeString: now.toTimeString(), /* Hora legível */
toLocaleDateString: now.toLocaleDateString(), /* Data local */
toLocaleTimeString: now.toLocaleTimeString(), /* Hora local */
};
}
/* Retorna o objeto de resultados */
return result;
}
/**
* Verifica se uma URL existe.
* @async
* @function urlExists
* @param {string} [url=envInfo.functions.urlexist.arguments.url.value] - A URL a ser verificada.
* @returns {Promise<boolean>} `true` se a URL existe, `false` caso contrário.
*/
async function urlExists(
url = envInfo.functions.urlexist.arguments.url.value,
) {
/* Define um resultado padrão */
envInfo.results.success = false;
envInfo.results.value = false;
/* Se der erro no request */
try {
/* Verifica se é uma string */
if (typeof url === 'string') {
/* Adquire a resposta do axios */
const response = await axios.head(url);
/* Se for diferente de 4**, existe, logo true */
envInfo.results.value = !/4\d\d/.test(response.status);
}
/* Define o sucesso */
envInfo.results.success = true;
/* Se deu erro */
} catch (error) { /* Faz nada */ }
/* Retorna os resultados */
return logging.postResults(envInfo);
}
/**
* Obtém uma patente com base no nível fornecido.
* @function getPatent
* @param {string|number} [level=envInfo.functions.patent.arguments.level.value] - O nível da patente.
* @param {Object} [patents=envInfo.functions.patent.arguments.patents.value] - O objeto contendo as patentes disponíveis.
* @returns {*} A patente correspondente ao nível ou a patente padrão (`patents['0']`).
*/
function getPatent(
level = envInfo.functions.patent.arguments.level.value,
patents = envInfo.functions.patent.arguments.patents.value,
) {
/* Define um resultado padrão */
envInfo.results.value = patents['0'];
/* Define o sucesso */
envInfo.results.success = false;
/* Define o nível */
let leveler = typeof level === 'string' || typeof level === 'number' ? level : 500;
/* Try-Catch para casos de erro */
try {
/* Se recebeu tudo corretamente, se der ruim, não fará nada */
if (/[0-9]+/gi.test(leveler) && typeof patents === 'object') {
/* Se o level for acima de 500, define como a patente mais alta */
leveler = leveler > 500 ? 500 : leveler;
/* Encontre o nível adequado usando a lógica */
const nivel = Object.keys(patents).find((n) => Number(leveler) <= Number(n));
/* Insere o resultado na envInfo para retornar tudo */
envInfo.results.value = patents[nivel || '0'];
}
/* Define o sucesso */
envInfo.results.success = true;
/* Caso de algum erro */
} catch (error) {
/* Insere tudo na envInfo */
logging.echoError(error, envInfo, __dirname);
}
/* Retorna os resultados */
return logging.postResults(envInfo);
}
/**
* Função que corrige o replace de variáveis em um objeto.
* @function replaceSystem
* @param {Object} obj - O objeto onde a substituição será realizada.
* @param {string} match - O padrão de correspondência para substituição.
* @param {string} key - A chave para localizar o valor no objeto.
* @returns {string} O valor substituído ou '?' se não encontrado.
*/
function replaceSystem(obj, match, key) {
/* Define as keys */
const parsedKeys = key.split('.');
/* Faz o parse */
const netterValue = parsedKeys.reduce((result, onmed, idlen) => {
/* Define o resultado */
const resRed = result[onmed] ?? 'N/A';
/* Formata a Array, une e retorna */
if (Array.isArray(resRed)) return resRed.map((val, idx) => `> *${idx}.* _'${val}'_\n`).join('');
/* Se for Object */
if (typeof resRed === 'object' && resRed !== null && !!resRed && parsedKeys.length === idlen + 1) {
/* Retorna uma String formatada com Object e join */
return Object.keys(resRed).map((val, idx) => {
/* Se for Obj-in-Obj */
if (typeof resRed[val] === 'object' && resRed[val] !== null && !!resRed[val]) {
/* Retorna a OIB com join */
return Object.keys(resRed[val]).map((dat) => {
/* Se for outra Object-in-Object */
if (typeof resRed[val][dat] === 'object' && resRed[val][dat] !== null && !!resRed[val][dat]) {
/* Retorna outra OIB com join */
return Object.keys(resRed[val][dat]).map((fns) => {
/* Se for outra Object-in-Object */
if (typeof resRed[val][dat][fns] === 'object' && resRed[val][dat][fns] !== null && !!resRed[val][dat][fns]) {
/* Retorna outra OIB com join */
return Object.keys(resRed[val][dat][fns]).map((ttd) => `\n> *${idx}.* _⟶ '${onmed}' → '${val}' → '${dat}' → '${fns}' → '${ttd}' →_ *'${resRed[val][dat][fns][ttd]}'*`).join('');
}
/* Retorna a OIB padrão */
return `\n> *${idx}.* _⟶ '${onmed}' → '${val}' → '${dat}' → '${fns}' →_ → *'${resRed[val][dat][fns]}'*`;
}).join('');
}
/* Retorna a OIO padrão */
return `\n> *${idx}.* _⟶ '${onmed}' → '${val}' → '${dat}' →_ *'${resRed[val][dat]}'*`;
}).join('');
}
/* Se não, retorna a Object padrão */
return `\n> *${idx}.* _⟶ '${onmed}' → '${val}' →_ *'${resRed[val]}'*`;
}).join('');
}
/* Retorna a key */
return resRed;
}, obj);
/* Retorna se o valor não for null/undefined */
if (netterValue != null) return netterValue;
/* Retorna '?' por padrão */
return '?';
}
/**
* Função base para localizar uma chave em um objeto.
* @function locateObjectKey
* @param {Object} obj - O objeto onde a chave será buscada.
* @param {Array<string>} keys - As chaves a serem localizadas.
* @param {Array<string>} types - Os tipos de dados permitidos para os valores.
* @param {Array<string>} ignores - As chaves a serem ignoradas.
* @param {number} [depth=0] - A profundidade atual da busca.
* @param {Set} [visited=new Set()] - Conjunto de objetos já visitados para evitar referências circulares.
* @returns {*} O valor da chave encontrada ou `false` se não encontrado.
*/
function locateObjectKey(obj, keys, types, ignores, depth = 0, visited = new Set()) {
/* Define o resultado padrão */
let keyInObject = false;
/* Verifica se a profundidade máxima foi atingida ou se é circular */
if (depth > 100 || visited.has(obj)) return keyInObject;
/* Marca o objeto como visitado */
visited.add(obj);
/* Busca a partir de um loop */
Object.keys(obj).some((prop) => {
/* Se já tiver achado o valor */
if (!keyInObject) {
/* Se a propriedade já for o valor requisitado */
if (keys.includes(prop) && types.includes(typeof obj[prop]) && !ignores.includes(prop)) {
/* Retorna o valor da propriedade */
keyInObject = obj[prop];
/* Verifica se é um objeto */
} else if (typeof obj[prop] === 'object' && obj[prop] !== null && !ignores.includes(prop)) {
/* Executa novamente com o novo objeto */
keyInObject = locateObjectKey(obj[prop], keys, types, ignores, depth + 1, visited);
}
}
/* Retorna se o valor foi encontrado */
return keyInObject;
});
/* Retorna o que foi encontrado */
return keyInObject;
}
/**
* Função para filtrar a chave de um objeto.
* @function findProperty
* @param {Object} [myObject=envInfo.functions.findkey.arguments.myObject.value] - O objeto onde a chave será buscada.
* @param {Array<string>} [findKey=envInfo.functions.findkey.arguments.findKey.value] - As chaves a serem localizadas.
* @param {Array<string>} [typeKey=envInfo.functions.findkey.arguments.typeKey.value] - Os tipos de dados permitidos para os valores.
* @param {Array<string>} [ignoreKeys=envInfo.functions.ignoreKeys.arguments.typeKey.value] - As chaves a serem ignoradas.
* @returns {*} O valor da chave encontrada ou `false` se não encontrado.
*/
function findProperty(
myObject = envInfo.functions.findkey.arguments.myObject.value,
findKey = envInfo.functions.findkey.arguments.findKey.value,
typeKey = envInfo.functions.findkey.arguments.typeKey.value,
ignoreKeys = envInfo.functions.ignoreKeys.arguments.typeKey.value,
) {
/* Define um resultado padrão */
envInfo.results.value = false;
/* Define o sucesso */
envInfo.results.success = false;
/* Try-Catch para lidar com possíveis erros */
try {
/* Executa apenas se os tipos estiverem corretos */
if (typeof myObject === 'object' && Array.isArray(findKey) && Array.isArray(typeKey) && Array.isArray(ignoreKeys)) {
/* Verifica se o objeto tem a chave desejada */
envInfo.results.value = locateObjectKey(myObject, findKey, typeKey, ignoreKeys);
}
/* Define o sucesso */
envInfo.results.success = true;
/* Em caso de erro, captura e registra no envInfo */
} catch (error) {
/* Registra o erro no envInfo */
logging.echoError(error, envInfo, __dirname);
}
/* Retorna o novo valor */
return logging.postResults(envInfo);
}
/**
* Aplica um efeito de blur e ajusta o brilho em uma imagem.
* @async
* @function blurImage
* @param {Buffer} [imageBuffer=envInfo.functions.blur.arguments.imageBuffer.value] - O buffer da imagem.
* @param {number} [blurL=envInfo.functions.blur.arguments.blurL.value] - O nível de blur a ser aplicado.
* @param {number} [brightL=envInfo.functions.blur.arguments.brightL.value] - O nível de brilho a ser ajustado.
* @returns {Promise<Buffer>} O buffer da imagem com os efeitos aplicados.
*/
async function blurImage(
imageBuffer = envInfo.functions.blur.arguments.imageBuffer.value,
blurL = envInfo.functions.blur.arguments.blurL.value,
brightL = envInfo.functions.blur.arguments.brightL.value,
) {
/* Define um resultado padrão */
envInfo.results.value = Buffer.from(envInfo.parameters.buffer.value);
/* Define o sucesso */
envInfo.results.success = false;
/* Try-Catch para casos de erro */
try {
/* Verifica se é um Buffer */
if (imageBuffer instanceof Buffer) {
/* Define o brilho padrão */
let brightLevel = 0.5;
/* Define o brilho padrão */
let blurLevel = 5;
/* Verifica se os valores são válidos */
if (isNumeric.test(blurL)) {
/* Ajusta o nível de blur */
blurLevel = Number(blurL);
}
/* Verifica se os valores são válidos */
if (isNumeric.test(brightL)) {
/* Ajusta o nível de blur */
brightLevel = Number(brightL);
}
/* Edita a imagem e adquire o Buffer */
/* Adiciona blur, faz modulação com nivel de brilho e finalmente converte em buffer */
envInfo.results.value = await sharp(imageBuffer).blur(blurLevel).modulate({ brightness: brightLevel }).toBuffer();
}
/* Define o sucesso */
envInfo.results.success = true;
/* Caso de algum erro */
} catch (error) {
/* Insere tudo na envInfo */
logging.echoError(error, envInfo, __dirname);
}
/* Retorna a nova Array */
return logging.postResults(envInfo);
}
/**
* Adquire o Buffer de uma URL.
* @async
* @function getBuffer
* @param {string} [imageURL=envInfo.functions.buffer.arguments.imageURL.value] - A URL da imagem.
* @returns {Promise<Buffer>} O buffer da imagem.
*/
async function getBuffer(
imageURL = envInfo.functions.buffer.arguments.imageURL.value,
) {
/* Define um resultado padrão */
envInfo.results.value = Buffer.from(envInfo.parameters.buffer.value);
/* Define o sucesso */
envInfo.results.success = false;
/* Try-Catch para casos de erro */
try {
/* Verifica se a URL é válida */
if (Indexer('regexp').urls(imageURL).value.isURL) {
/* Faz a requisição do URL */
const response = await axios.get(imageURL, {
/* Em ArrayBuffer */
responseType: 'arraybuffer',
});
/* Converte para Buffer */
envInfo.results.value = Buffer.from(response.data, 'utf-8');
}
/* Define o sucesso */
envInfo.results.success = true;
/* Caso de algum erro */
} catch (error) {
/* Insere tudo na envInfo */
logging.echoError(error, envInfo, __dirname);
}
/* Retorna a nova Array */
return logging.postResults(envInfo);
}
/**
* Substitui texto em um objeto.
* @function replaceText
* @param {Object} obj - O objeto onde o texto será substituído.
* @param {string} ftext - O texto a ser substituído.
* @param {string} ntext - O novo texto.
* @returns {Object} O objeto com o texto substituído.
*/
function replaceText(obj, ftext, ntext) {
/* Cria uma cópia do objeto original */
const defObject = { ...obj };
/* Itera sobre as chaves do objeto */
Object.keys(defObject).forEach((objkey) => {
/* Se tiver e for string */
if (typeof defObject[objkey] === 'string') {
/* Substitui o texto */
defObject[objkey] = defObject[objkey].replace(ftext, ntext).replace(new RegExp(ftext, 'gi'), ntext);
/* Se não, mas for um objeto, continua a busca */
} else if (typeof defObject[objkey] === 'object') {
/* Enviando ela para rodar novamente a função */
defObject[objkey] = replaceText(defObject[objkey], ftext, ntext);
}
});
/* Retorna o objeto modificado */
return defObject;
}
/**
* Substitui texto em todas as ocorrências dentro de um objeto.
* @function replaceInAll
* @param {Object} [myObject=envInfo.functions.replaceInAll.arguments.myObject.value] - O objeto onde o texto será substituído.
* @param {string} [findText=envInfo.functions.replaceInAll.arguments.findText.value] - O texto a ser substituído.
* @param {string} [newText=envInfo.functions.replaceInAll.arguments.newText.value] - O novo texto.
* @returns {Object} O objeto com o texto substituído.
*/
function replaceInAll(
myObject = envInfo.functions.replaceInAll.arguments.myObject.value,
findText = envInfo.functions.replaceInAll.arguments.findText.value,
newText = envInfo.functions.replaceInAll.arguments.newText.value,
) {
/* Define um resultado padrão */
envInfo.results.value = myObject;
/* Define o sucesso */
envInfo.results.success = false;
/* Try-Catch para casos de erro */
try {
/* Inicia o processo e define o valor */
envInfo.results.value = replaceText(myObject, findText, newText);
/* Define o sucesso */
envInfo.results.success = true;
/* Caso de algum erro */
} catch (error) {
/* Insere tudo na envInfo */
logging.echoError(error, envInfo, __dirname);
}
/* Retorna a nova Array */
return logging.postResults(envInfo);
}
/**
* Faz a função esperar "x" tempo antes de avançar.
* @async
* @function sleep
* @param {number} [miliseconds=envInfo.functions.sleep.arguments.miliseconds.value] - O tempo de espera em milissegundos.
* @returns {Promise<number>} O tempo de espera em milissegundos.
*/
async function sleep(
miliseconds = envInfo.functions.sleep.arguments.miliseconds.value,
) {
/* Define um resultado padrão | MS */
envInfo.results.value = miliseconds;
/* Verifica se o valor não é número */
if (!isNumeric.test(miliseconds)) {
/* Define uma espera padrão */
envInfo.results.value = envInfo.functions.sleep.arguments.miliseconds.value;
}
/* Define o sucesso */
envInfo.results.success = false;
/* Try-Catch para casos de erro */
try {
/* Faz a função esperar o tempo antes de retornar resolve */
await new Promise((resolve) => {
/* Espera a função finalizar */
setTimeout(resolve, envInfo.results.value);
});
/* Define o sucesso */
envInfo.results.success = true;
/* Caso de algum erro */
} catch (error) {
/* Insere tudo na envInfo */
logging.echoError(error, envInfo, __dirname);
}
/* Retorna a nova Array */
return logging.postResults(envInfo);
}
/**
* Transforma o grupo num tipo de seletor por números.
* @function createList
* @param {Array} [chatIDs=envInfo.functions.list.arguments.chatIDs.value] - A lista de IDs de chat.
* @returns {Object} Um objeto indexado com os valores dos chats.
*/
function createList(
chatIDs = envInfo.functions.list.arguments.chatIDs.value,
) {
/* Define um resultado padrão */
envInfo.results.value = {};
/* Define o sucesso */
envInfo.results.success = false;
/* Try-Catch para casos de erro */
try {
/* Caso o ChatIDs seja válido */
if (Array.isArray(chatIDs)) {
/* Formata Chat por Chat */
envInfo.results.value = chatIDs.map((val, idx) => (
/* Retorna um objeto indexado com os valores */
{
[idx]: {
id: val?.id ?? idx,
name: val?.name ?? 'unknown',
},
}
));
/* Transforma em Object */
envInfo.results.value = Object.assign({}, ...envInfo.results.value);
}
/* Define o sucesso */
envInfo.results.success = true;
/* Caso de algum erro */
} catch (error) {
/* Insere tudo na envInfo */
logging.echoError(error, envInfo, __dirname);
}
/* Retorna a nova Array */
return logging.postResults(envInfo);
}
/**
* Restaura o ambiente e atualiza as exportações do módulo com a funcionalidade principal
* @param {Object} [changeKey={}] - Chaves personalizadas para atualizar o envInfo
* @param {Object} [envFile=envInfo] - Objeto com informações do ambiente
* @param {string} [dirname=__dirname] - Caminho do diretório atual
* @returns {Object} Exportações do módulo com todas as funções configuradas
*/
/* eslint-disable-next-line no-return-assign */
const resetLocal = (
changeKey = {},
envFile = envInfo,
dirname = __dirname,
) => module.exports = logging.resetAmbient({
functions: {
[envInfo.exports.env]: { value: ambientDetails },
[envInfo.exports.messedup]: { value: logging.echoError },
[envInfo.exports.poswork]: { value: logging.postResults },
[envInfo.exports.reset]: { value: resetLocal },
[envInfo.exports.farpc]: { value: replaceInAll },
[envInfo.exports.blur]: { value: blurImage },
[envInfo.exports.buffer]: { value: getBuffer },
[envInfo.exports.merge]: { value: deepMerge },
[envInfo.exports.sleep]: { value: sleep },
[envInfo.exports.list]: { value: createList },
[envInfo.exports.date]: { value: isValidDate },
[envInfo.exports.patent]: { value: getPatent },
[envInfo.exports.repl]: { value: replaceSystem },
[envInfo.exports.findkey]: { value: findProperty },
[envInfo.exports.urlexists]: { value: urlExists },
},
parameters: {
location: { value: __filename },
},
}, envFile, changeKey, dirname);
resetLocal();