Source: Commands/Main/Construct/index.js

/* eslint-disable max-len */

/* Requires */
const fs = require('fs');
const removeAccents = require('remove-accents');
const { extractMetadata } = require('wa-sticker-formatter');
const issimilar = require('similarity');
const { downloadMediaMessage, downloadContentFromMessage, getDevice } = require('baileys');
const Indexer = require('../../../index');

/* JSON's | Utilidades */
const envInfo = JSON.parse(fs.readFileSync(`${__dirname}/utils.json`));
const commands = JSON.parse(fs.readFileSync('./lib/Databases/Configurations/definitions.json'));
const levelSettings = JSON.parse(fs.readFileSync('./lib/Databases/Configurations/leveling.json'));
let arrayOfCommands = JSON.parse(Indexer('bash').bash(`bash "${irisPath}/lib/Scripts/Others/Menu.sh" "array"`).value);
const oldLanguage = region;
let conditions = false;

/* Insere todos os comandos na arrayOfCommands */
arrayOfCommands = [...arrayOfCommands, Object.values(envInfo.parameters.alias.value).flat(5)].flat(5);
arrayOfCommands = [...new Set(arrayOfCommands)];

/**
 * 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;
}

/* Faz a formatação e coleta dos dados */
async function dataCollect(
    kill = envInfo.functions.make.arguments.kill.value,
    message = envInfo.functions.make.arguments.message.value,
    msgbackup = envInfo.functions.make.arguments.msgbackup.value,
) {
    /* Define um resultado padrão */
    envInfo.results.value = false;

    /* Define o sucesso */
    envInfo.results.success = false;

    /* Define a Object padrão */
    let messageData = {
        actualMoment: Date.now(),
        readDate: (msgbackup.currentTimeDate / 1000).toFixed(0),
        levelSettings,
        allCommands: arrayOfCommands,
    };

    /* Try-Catch para casos de erro */
    try {
        /* Determina se algum parâmetro veio errado */
        if (typeof kill === 'object' && typeof message === 'object') {
            /* Define a mensagem a marcar */
            messageData.quoteThis = message || {};

            /* Define o Object pronto da mensagem a marcar */
            messageData.reply = { quoted: messageData.quoteThis };

            /* Define as condições iniciais */
            conditions = (
                /* Se a mensagem não for uma Object (possivel evento) */
                !typeof messageData.quoteThis?.message === 'object'

                /* Se não tiver mensagem (possivel evento) */
                || messageData.quoteThis?.message == null

                /* Se for um evento e não tiver indicador de mensagem */
                || (msgbackup.type === 'append' && Object.keys(messageData.quoteThis.message).every((ms) => !ms.includes('Message')))
            );

            /* Impede de continuar se a mensagem estiver com problema | Permite se for evento, impede se der problema no começo */
            if ((conditions && config.allowEvents.value === false) || messageData.quoteThis == null) return logging.postResults(envInfo);

            /* Define uma Object de mensagem pra não matar o sistema */
            messageData.quoteThis.message = messageData?.quoteThis?.message || {};

            /* Se tiver ephemeralMessage ou viewOnce */
            if (Object.keys(messageData?.quoteThis?.message).includes('ephemeralMessage') || Object.keys(messageData?.quoteThis?.message).includes('viewOnce')) {
                /* Define um backup da mensagem para uso externo */
                messageData.quoteThis.backupMessage = msgbackup.messages;

                /* Define a mensagem ephemeral na object message */
                messageData.quoteThis.message = messageData.quoteThis.message?.ephemeralMessage?.message || messageData.quoteThis.message?.viewOnceMessage?.message || messageData.quoteThis.message?.viewOnceMessageV2?.message || messageData.quoteThis.message?.viewOnceMessage?.message || messageData.quoteThis.message?.viewOnceMessageV2 || messageData.quoteThis.message?.ephemeralMessage;
            }

            /* Define a medição de tempo */
            messageData.timestamp = messageData.quoteThis.messageTimestamp || Date.now();

            /* Define o Ping avançado */
            messageData.pingTime = Indexer('number').format(messageData.readDate - messageData.timestamp).overall;

            /* Define o Ping */
            messageData.procTime = Indexer('number').format(messageData.readDate - messageData.timestamp).seconds;

            /* Define se é editada */
            messageData.editedMessageObj = Indexer('others').findkey(messageData.quoteThis, ['editedMessage'], ['object'], ['contextInfo', 'quotedMessage']).value;
            messageData.editedMessage = !!messageData?.editedMessageObj;

            /* Define a key da mensagem */
            const chatMessage = messageData.quoteThis.key;
            messageData.messageKey = chatMessage;

            /* Roda o sistema de eventos */
            const eventRunner = await Indexer('events').execute(kill, messageData.quoteThis);

            /* Define as condições */
            conditions = (
                /* Mensagem de si mesmo, mas não permitido pelo dono */
                (chatMessage?.fromMe && config.botCommands.value === false)

                /* Mensagens editadas */
                || messageData.editedMessage

                /* Atualizações de enquetes */
                || (Object.keys(messageData.quoteThis?.message).includes('pollUpdate') && config.listenPolls.value === false)

                /* Transmissões */
                || (messageData.quoteThis.broadcast === true && config.listenBroadcasts.value === false)

                /* Reações em mensagens */
                || (Object.keys(messageData.quoteThis?.message).includes('reactionMessage') && config.listenReactions.value === false)

                /* Se o evento mandar parar */
                || eventRunner.value === 'STOP'
            );

            /* Define se deve ignorar o processamento das mensagens da Íris, editadas, broadcast e reações como mensagens normais */
            if (conditions) {
                /* Define soft error */
                envInfo.results.success = 'DONTRUNTHIS';
                envInfo.results.value = {};

                /* Retorna algo */
                return envInfo.results;
            }

            /* Busca avançada de object para localizar a perfeita, faz isso em 4 niveis de busca */
            const messageKeys = ['audioMessage', 'bcallMessage', 'botInvokeMessage', 'buttonsMessage', 'buttonsResponseMessage', 'contactMessage', 'conversation', 'contactsArrayMessage', 'documentMessage', 'documentWithCaptionMessage', 'editedMessage', 'ephemeralMessage', 'extendedTextMessage', 'groupInviteMessage', 'groupMentionedMessage', 'imageMessage', 'interactiveMessage', 'interactiveResponseMessage', 'invoiceMessage', 'listMessage', 'listResponseMessage', 'liveLocationMessage', 'locationMessage', 'lottieStickerMessage', 'messageHistoryBundle', 'newsletterAdminInviteMessage', 'orderMessage', 'pollCreationMessage', 'pollCreationMessageV2', 'pollCreationMessageV3', 'pollUpdateMessage', 'productMessage', 'protocolMessage', 'ptvMessage', 'reactionMessage', 'requestPaymentMessage', 'scheduledCallCreationMessage', 'scheduledCallEditMessage', 'sendPaymentMessage', 'senderKeyDistributionMessage', 'stickerMessage', 'templateButtonReplyMessage', 'templateMessage', 'videoMessage', 'viewOnceMessage', 'viewOnceMessageV2', 'viewOnceMessageV2Extension'];
            let recMessage = messageData?.quoteThis;
            recMessage = Indexer('others').findkey(recMessage, messageKeys, ['object', 'string'], ['contextInfo', 'quotedMessage']).value || recMessage;
            recMessage = Indexer('others').findkey(recMessage, messageKeys, ['object', 'string'], ['contextInfo', 'quotedMessage']).value || recMessage;
            recMessage = Indexer('others').findkey(recMessage, messageKeys, ['object', 'string'], ['contextInfo', 'quotedMessage']).value || recMessage;
            recMessage = Indexer('others').findkey(recMessage, messageKeys, ['object', 'string'], ['contextInfo', 'quotedMessage']).value || recMessage;
            messageData.recMessage = recMessage;

            /* Define se é ViewOnce */
            const viewOnceObj = messageData.quoteThis.message?.viewOnceMessageV2 || messageData.quoteThis.message?.viewOnceMessage;
            const isViewOnce = messageData.quoteThis.message?.viewOnceMessageV2 !== null || messageData.quoteThis.message?.viewOnceMessage !== null || recMessage?.viewOnce || messageData.typeLower === 'viewOnceMessageV2';
            /* eslint-disable-next-line no-nested-ternary */
            const viewOnce = (isViewOnce && viewOnceObj) ? viewOnceObj[Object.keys(viewOnceObj)[0]] : (isViewOnce ? recMessage : false);
            messageData.viewOnce = viewOnce;
            messageData.isViewOnce = isViewOnce;

            /* Se for mensagem editada */
            messageData.quoteThis = messageData.editedMessage ? recMessage : messageData.quoteThis;

            /* Por mais que eu não goste disso, o try-catch aqui é essencial para lidar com mensagens bugadas que o Baileys envia, como a futureProofMessage (???) */
            try {
                /* Define o peso se não tiver */
                recMessage.fileLength = recMessage?.fileLength || { low: 1000, high: 0, unsigned: false };

                /* Se der erro */
            } catch (err) {
                /* Printa a mensagem pedindo para criar um report, pois essa informação será de extrema utilidade para correções */
                console.log('\x1b[31m', 'REPORT THIS TO PROJECT ÍRIS [https://github.com/KillovSky/Iris]!\n', `\x1b[33m${JSON.stringify(message)}`);

                /* Printa o erro */
                console.error(err);

                /* Define soft error */
                envInfo.results.success = 'DONTRUNTHIS';
                envInfo.results.value = {};

                /* Retorna algo */
                return envInfo.results;
            }

            /* Define a mensagem na envInfo */
            messageData.basemessage = message;

            /* Define a mídia criptografada na envInfo */
            messageData.encryptMedia = recMessage;

            /* Define a marcação de mensagem */
            messageData.quotedMsg = Indexer('others').findkey(recMessage, ['contextInfo'], ['object'], []).value || {};
            messageData.isQuotedMsg = Object.keys(messageData.quotedMsg).length > 1;

            /* Define a marcação de mensagem com objeto e se é viewOnce */
            messageData.quotedMsgObj = messageData.quotedMsg?.quotedMessage ? messageData.quotedMsg?.quotedMessage[Object.keys(messageData.quotedMsg.quotedMessage)[0]] : {};
            const quotedViewOnce = messageData.quotedMsgObj?.message;

            /* Se for uma mensagem marcada com key Message ainda, reduz novamente */
            messageData.quotedMsgObj = quotedViewOnce ? quotedViewOnce[Object.keys(quotedViewOnce)[0]] : messageData.quotedMsgObj;
            messageData.isQuotedViewOnce = messageData.quotedMsgObj?.viewOnce;

            /* Se a quoted object estiver vazia, define como a quotedMessage padrão */
            messageData.quotedMsgObj = messageData.isQuotedMsg && messageData.quotedMsgObj == null ? messageData.quotedMsg : messageData.quotedMsgObj;

            /* Determina se é uma mídia */
            messageData.isMedia = !!recMessage?.mimetype;
            messageData.decryptFormats = ['ppic', 'product', 'image', 'video', 'sticker', 'audio', 'gif', 'ptt', 'thumbnail-document', 'thumbnail-image', 'thumbnail-link', 'thumbnail-video', 'md-app-state', 'md-msg-hist', 'document', 'product-catalog-image', 'payment-bg-image'];

            /* Determina o tipo de mensagem */
            messageData.type = Object.keys(messageData.quoteThis.message)[0] || 'conversation';
            messageData.typeFormatted = messageData.type.replace('Message', '');
            messageData.typeLower = messageData.typeFormatted.toLowerCase();
            messageData.typeDecrypt = messageData.typeLower;

            /* Define a hora de agora */
            messageData.dateOfDay = (new Date()).getHours();

            /* Define o tempo formatado */
            messageData.time = new Date(messageData.timestamp * 1000).toLocaleString();

            /* Define o tempo de execução de debug */
            messageData.debugExec = Indexer('number').format((Date.now() / 1000) - messageData.readDate).seconds;

            /* Comando debug */
            if (recMessage?.caption === '/debugping' || recMessage === '/debugping' || recMessage?.text === '/debugping') return await kill.sendMessage((chatMessage?.remoteJid || chatMessage?.participant), { text: `Debug (Read): ${messageData.procTime}s\nDebug (Exec): ${messageData.debugExec.toFixed(6)}s` }, messageData.reply);

            /* --------------------- Mensagem --------------------------- */

            /* Define o usuário */
            messageData.sender = chatMessage?.participant || {};

            /* Determina a Chat */
            messageData.chat = chatMessage?.remoteJid.includes('@g.us') ? await kill.groupMetadata(chatMessage?.remoteJid) : {};

            /* Define o nome do grupo */
            messageData.originalName = messageData.chat?.subject || '';
            messageData.name = messageData.originalName;

            /* Define o número de quem enviou */
            messageData.user = chatMessage?.participant || chatMessage?.remoteJid || '';

            /* Define uma user formatada */
            messageData.userFormated = messageData.user.replace(/@s.whatsapp.net|@g.us/gi, '');

            /* Define o nome de usuário */
            messageData.originalPushname = message?.pushName || '"Censored by Government"';
            messageData.pushname = messageData.originalPushname;

            /* Se ativarem o fixEscape, faz ajuste para o SQLite */
            if (config.fixEscape.value) {
                /* Ativa o escape adicional para evitar erros */
                /* eslint-disable-next-line no-useless-escape */
                messageData.pushname = messageData.originalPushname.replace(/[`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '');

                // eslint-disable-next-line no-useless-escape
                messageData.name = messageData.originalName.replace(/[`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '');
            }

            /* Determina a ID do Chat */
            messageData.chatId = chatMessage?.remoteJid || messageData.user || '';
            messageData.chatId = messageData.chatId === 'status@broadcast' ? messageData.user : messageData.chatId;

            /* Determina se é um grupo */
            messageData.isGroup = messageData.chatId.includes('@g.us') || false;

            /* Redefine o nome do grupo se for PV */
            messageData.name = messageData.isGroup ? messageData.name : 'PV';

            /* Determina se é uma mensagem em grupo */
            messageData.isGroupMsg = messageData.isGroup;

            /* Define a ID */
            messageData.id = chatMessage?.id || '';

            /* Cria uma cópia da ID por segurança */
            messageData.serial = messageData.id;

            /* Define o mimetype */
            messageData.mimetype = recMessage?.mimetype || '';

            /* Define se é um contato */
            messageData.isContact = messageData.typeLower === 'contact' || messageData.mimetype?.toLowerCase().includes('contact');

            /* Define se é uma array de contatos */
            messageData.isContactArray = messageData.typeLower === 'contactsArray';

            /* Define se é uma localização live */
            messageData.isLiveLocation = messageData.typeLower === 'liveLocation';

            /* Define se é uma localização */
            messageData.isLocation = messageData.typeLower === 'location';

            /* Define se é uma reação */
            messageData.isReaction = messageData.typeLower === 'reaction';

            /* Define se é vídeo */
            messageData.isVideo = messageData.typeLower === 'video' || messageData.mimetype?.toLowerCase().includes('video');

            /* Define se é imagem */
            messageData.isImage = messageData.typeLower === 'image' || messageData.mimetype?.toLowerCase().includes('image');

            /* Define se é áudio */
            messageData.isAudio = messageData.typeLower === 'audio' || messageData.mimetype?.toLowerCase().includes('audio');

            /* Define se é um documento */
            messageData.isDocument = messageData.typeLower === 'document' || messageData.typeLower === 'documentWithCaption' || messageData.mimetype?.toLowerCase().includes('application') || messageData.mimetype?.toLowerCase().includes('document') || messageData.mimetype?.toLowerCase().includes('text');

            /* Define se pode ser feito sticker disso */
            messageData.canSticker = messageData.isImage || messageData.isVideo || false;

            /* Define o sistema de envio para cada tipo */
            messageData.sendingKey = messageData.typeLower === 'conversation' ? 'text' : messageData.typeLower;
            messageData.sendingKey = messageData.isAudio ? 'audio' : messageData.sendingKey;
            messageData.sendingKey = messageData.isImage ? 'image' : messageData.sendingKey;
            messageData.sendingKey = messageData.isVideo ? 'video' : messageData.sendingKey;
            messageData.sendingKey = messageData.isDocument ? 'document' : messageData.sendingKey;
            messageData.sendingKey = messageData.isLiveLocation ? 'location' : messageData.sendingKey;
            messageData.sendingKey = messageData.isContact || messageData.isContactArray ? 'contact' : messageData.sendingKey;

            /* Reajusta o tipo de mensagem simplificada para decriptar */
            messageData.typeDecrypt = messageData.sendingKey;

            /* -------------------- Functions ----------------------- */

            /* Define os JSONs */
            const stockDefault = Indexer('sql').env().parameters.standard.value;
            let functions = stockDefault.Groups;
            let {
                Leveling,
                Bank,
            } = stockDefault;

            /* Dados pessoais */
            messageData.personal = Indexer('sql').get('personal', messageData.user, messageData.chatId).value;

            /* Se for DB antiga */
            if (messageData.personal.name.number === '0') {
                /* Atualiza o número */
                messageData.personal = Indexer('sql').update('personal', messageData.user, messageData.chatId, 'name', { number: messageData.userFormated }).value;
            }

            /* Determina o arquivo de dados */
            if (messageData.isGroup === true && /@g.us/gi.test(messageData.chatId) && /@s.whatsapp.net/g.test(messageData.user)) {
                /* Ajusta a leveling para o valor real */
                Leveling = Indexer('sql').get('leveling', messageData.user, messageData.chatId).value;

                /* Ajusta a bank para o valor correto */
                Bank = Indexer('sql').get('bank', messageData.user, messageData.chatId).value;

                /* Adquire as funções de grupo ativas */
                functions = Indexer('sql').get('groups', messageData.user, messageData.chatId).value;

                /* Adiciona +1 no contador de mensagens, e 10 no XP */
                Indexer('sql').update('leveling', messageData.user, messageData.chatId, 'messages', 1);
                Indexer('sql').update('leveling', messageData.user, messageData.chatId, 'xp', Number(levelSettings.messageXP.value));
            }

            /* Redefine o idioma */
            region = oldLanguage;

            /* Define a região com base nas condições fornecidas */
            if (functions.language?.enable && Indexer('sql').languages('G.A.L').includes(functions.language?.text)) {
                /* Define como o armazenado nas funções de grupo */
                region = functions.language?.text;
            }

            /* Se for PV e tiver ativado a language */
            if (messageData.personal.language?.enable && Indexer('sql').languages('G.A.L').includes(messageData.personal.language?.text)) {
                /* Define com base no valor padrão */
                region = messageData.personal.language?.text;
            }

            /* Define o nome da pessoa, caso ainda esteja em default, se um dia não der pra obter o nome, esse nome aqui será usado */
            if (messageData.personal.name?.text !== 'default' && messageData?.pushname === '"Censored by Government"') {
                /* Nome pela database */
                messageData.pushname = messageData.personal.name?.text;

                /* Se a database estiver desatualizada e o pushname puder ser obtido */
            } else if ((messageData.personal.name.text === 'default' || messageData.personal.name.text !== messageData.pushname) && messageData.pushname !== '"Censored by Government"') {
                /* Atualiza a database com o nome atual */
                messageData.personal = Indexer('sql').update('personal', messageData.user, messageData.chatId, 'name', {
                    text: messageData.pushname, lastDate: Date.now(), number: messageData.userFormated, lastValue: messageData.personal.name.text, firstEdition: messageData.personal.name.firstEdition || messageData.personal.name.text, firstDate: messageData.name.firstDate || Date.now(),
                }).value;
            }

            /* Define o nome do grupo na DB caso tenha mudado */
            if (functions.name?.text !== messageData.name && messageData.isGroup === true) {
                /* Atualiza a database com o nome atual */
                functions = Indexer('sql').update('groups', messageData.user, messageData.chatId, 'name', {
                    text: messageData.name, lastDate: Date.now(), lastValue: functions.name.text,
                }).value;
            }

            /* --------------------- Body --------------------------- */

            /* Determina o texto, a array esta organizada de forma a obter na ordem dela, se achar a caption não vai buscar text e outros */
            const bodyKeys = ['caption', 'conversation', 'text', 'selectedId', 'selectedRowId', 'selectedButtonId', 'matchedText', 'displayName', 'title', 'fileName', 'selectedDisplayText', 'note', 'body', 'description'];
            messageData.body = Indexer('others').findkey(recMessage, bodyKeys, ['string', 'number'], ['contextInfo', 'quotedMessage']).value || Indexer('others').findkey(messageData.quoteThis, bodyKeys, ['string', 'number'], ['contextInfo', 'quotedMessage']).value || Indexer('others').findkey(message, bodyKeys, ['string', 'number'], ['contextInfo', 'quotedMessage']).value || '';
            messageData.body = messageData.typeLower === 'sticker' || (messageData.isMedia && !recMessage?.caption) ? '' : messageData.body;
            messageData.body = (messageData.isCmd && messageData.isQuotedMsg) || !messageData.isQuotedMsg || !messageData.isCmd ? messageData.body : '';

            /* Define se teve URLs */
            messageData.urlData = Indexer('regexp').urls(messageData.body).value;

            /* Define se a mensagem é um convite */
            messageData.isInvite = Indexer('regexp').invite(messageData.body).value || messageData.type === 'groupInviteMessage' || messageData.body?.includes('chat.whatsapp.net') || messageData.typeLower?.includes('groupInvite') || false;

            /* Cria uma copia da body para eventuais razões */
            messageData.message = messageData.body;

            /* Define os argumentos */
            messageData.arguments = messageData.body.split(/ +/);

            /* Define os argumentos sem o comando */
            messageData.args = messageData.arguments.slice(1);

            /* Junta os argumentos de texto */
            messageData.arg = messageData.args.join(' ');

            /* Define os argumentos em Lowercase */
            messageData.argl = messageData.args.map((al) => al.toLowerCase());

            /* Junta os argumentos em Lowercase */
            messageData.arks = messageData.argl.join(' ');

            /* Define os argumentos em Uppercase */
            messageData.argc = messageData.args.map((ac) => ac.toUpperCase());

            /* Junta os argumentos em Uppercase */
            messageData.arqc = messageData.argc.join(' ');

            /* Extrai os emojis do body */
            messageData.emojis = messageData.body.match(/[\p{Emoji}]/gu) || [];

            /* --------------------- Prefix --------------------------- */

            /* Determina o prefix padrão */
            let prefixes = config?.prefixes.value.filter((p) => p === messageData.body.slice(0, 1));
            messageData.prefix = prefixes[0] || '/';

            /* Verifica se é outro prefix */
            if (messageData.isGroupMsg) {
                /* Verifica se o prefix atual é parte */
                if (functions.prefix.enable === true) {
                    /* Verifica por prefixos */
                    prefixes = functions.prefix.values.filter((p) => p === messageData.body.slice(0, 1));

                    /* Define o customizado ou um da lista, impossibilitando rodar comandos sem ele */
                    messageData.prefix = prefixes[0] || functions.prefix.values[0] || '/';
                }
            }

            /* --------------------- Commands --------------------------- */

            /* Determina se é um comando */
            messageData.isCmd = functions?.autostickers?.enable && messageData.canSticker ? true : messageData.body.startsWith(messageData.prefix);

            /* Define a mensagem decriptada */
            messageData.decryptedMedia = messageData.isCmd && messageData.decryptFormats.includes(messageData.typeDecrypt) && ((recMessage?.fileLength?.low || recMessage?.fileLength?.high || 1000) / 1024) <= config.maxDecryptSize.value ? await downloadMediaMessage(messageData.quoteThis, 'buffer') : '';

            /* Adquire a primeira palavra e converte em lowercase */
            messageData.command = functions?.autostickers?.enable && messageData.canSticker ? `${messageData.prefix}sticker` : (messageData.body
                .trim()
                .split(/ +/)
                .shift()
                .toLowerCase()
            );

            /* Se for mesmo um comando, tira o prefix dele */
            messageData.command = (messageData.isCmd === true ? messageData.command.slice(messageData.prefix.length) : messageData.command);

            /* Remove os acentos dela */
            messageData.command = removeAccents(messageData.command);

            /* Determina a pasta de verificação por case */
            let commander = Object.keys(envInfo.parameters.alias.value).filter((cm) => envInfo.parameters.alias.value[cm].includes(messageData.command));
            commander = commander[0] || 'Default';

            /* Se for autosticker cria uma propriedade para ignorar a mensagem de aguarde do script */
            messageData.autoSticker = functions?.autostickers?.enable && messageData.canSticker;

            /* --------------------- ADMS/Donos/Membros --------------------------- */

            /* Faz a busca automatica por menções */
            const mentionFinder = Indexer('others').findkey(recMessage, ['mentionedJid'], ['array', 'object'], []);

            /* Define as menções */
            messageData.mentionedJidList = Indexer('others').findkey(recMessage, ['mentionedJid'], ['object', 'array'], []).value || [];
            messageData.mentionedJidList.push(messageData.quotedMsg?.participant || messageData.user);

            /* Insere o autor da mensagem também */
            messageData.mentionedJidList.push(messageData.user);

            /* Se houve menções */
            if (typeof mentionFinder.value === 'object') {
                /* Adiciona elas no inicio de tudo */
                messageData.mentionedJidList = [...mentionFinder.value, ...messageData.mentionedJidList];
            }

            /* Define formatada */
            messageData.mentionedJidListFormated = messageData.mentionedJidList.map((s) => s.replace(/@s.whatsapp.net|@/gi, ''));

            /* Ajusta tirando valores errados */
            messageData.mentionedJidListFormated = messageData.mentionedJidListFormated.filter((s) => /[0-9]+/gi.test(s));
            messageData.mentionedJidList = messageData.mentionedJidList.filter((s) => s.includes('@s.whatsapp.net'));

            /* Remove duplicações */
            messageData.mentionedJidListFormated = [...new Set(messageData.mentionedJidListFormated)];
            messageData.mentionedJidList = [...new Set(messageData.mentionedJidList)];

            /* Determina os membros padrões do grupo */
            messageData.groupMembersId = [messageData.user];

            /* Verifica apenas se for grupo */
            if (messageData.isGroupMsg === true) {
                /* Filtra os membros sem a função async */
                messageData.groupMembersId = messageData.chat?.participants?.map((prs) => prs.id) || [];
            }

            /* Define os IDs dos participantes do grupo, mas formatados */
            messageData.groupMembersIdFormated = messageData.groupMembersId.map((us) => us.replace(/@g.us|@s.whatsapp.net/gi, ''));

            /* Determina os ADMS padrões */
            messageData.groupAdmins = [];

            /* Verifica os ADMS apenas em grupo */
            if (messageData.isGroupMsg === true) {
                /* Filtra os ADMS sem a função async */
                messageData.groupAdmins = messageData.chat?.participants?.filter((prs) => prs?.admin === 'admin' || prs?.admin === 'superadmin') || [];

                /* Obtém somente os números */
                messageData.groupAdmins = messageData.groupAdmins.map((udm) => udm?.id) || [];
            }

            /* Define uma groupAdmins formatada */
            messageData.groupAdminsFormated = messageData.groupAdmins.map((ausr) => (ausr || '').replace(/@g.us|@s.whatsapp.net/gi, ''));

            /* Verifica se faz parte dos ADMS */
            messageData.isGroupAdmins = messageData.groupAdmins?.includes(messageData.user) || false;

            /* Verifica se a Íris é ADM */
            messageData.isBotGroupAdmins = messageData.groupAdmins?.includes(global.irisNumber) || false;

            /* Define se é o criador do grupo */
            messageData.groupCreator = messageData.chat?.participants?.filter((prs) => prs?.admin === 'superadmin')[0];
            messageData.isGroupCreator = messageData.groupCreator === messageData.user;

            /* Define se é o dono */
            messageData.isOwner = config?.owner?.value?.includes(messageData.user) || messageData.body.includes(config?.secretKey?.value) || false;

            /* Define se é a Íris */
            messageData.isBot = chatMessage?.fromMe || global.irisNumber === messageData.user || false;

            /* Define a plataforma do usuário */
            messageData.userPlatform = getDevice(messageData.id);

            /* --------------------- Membros --------------------------- */

            /* Adquire os bloqueados */
            messageData.blockNumber = await kill.fetchBlocklist();

            /* Define se o usuário está bloqueado */
            messageData.isBlocked = messageData.blockNumber.includes(messageData.user);

            /* --------------------- Sticker --------------------------- */

            /* Define uma let para não substituir a config */
            let stckAuth = config.stickerAuthor.value;

            /* Verifica se pode deixar os dados do sticker com nome do grupo */
            if (config.stickerAuthor.value.includes('DONTEDITUSER')) {
                /* Se sim, remove os acentos e troca pelo nome */
                stckAuth = removeAccents(config.stickerAuthor.value.replace(/DONTEDITFROM/g, messageData.name).replace(/DONTEDITUSER/g, messageData.pushname).replace(/DONTEDITBY/g, config.botName.value).replace(/DONTEDITMADEAS/g, new Date().toLocaleString()));
            }

            /* Define o Pack do Sticker */
            const stckPack = (removeAccents(config.stickerPack.value) || '🔰 Legião Z [linktr.ee/killovsky] Íris ⚜️');

            /*
                Metadados do Sticker
                type = 'default', 'crop', 'full', 'circle, 'rounded'
                background = {r:0,g:0,b:0,alpha:1}, #ffffff
            */
            messageData.stickerConfig = {
                author: stckAuth,
                pack: stckPack,
                type: undefined,
                categories: messageData.emojis[0] != null ? messageData.emojis : undefined,
                quality: 100,
                background: 'transparent',
                id: config.botName.value,
            };

            /* --------------------- Outros --------------------------- */

            /* Define se é um VIP */
            messageData.isVIP = false;
            messageData.isModerator = false;

            /* Verifica os mods e vips apenas se houver mensagem em grupo */
            if (messageData.isGroupMsg) {
                /* Define se é um VIP */
                messageData.isVIP = functions?.vips?.values?.includes(messageData.user.userFormated) && functions?.vips?.enable === true;

                /* Define se é um MOD */
                messageData.isModerator = functions?.mods?.values?.includes(messageData.userFormated) && functions?.mods?.enable === true;
            }

            /* Sugere um comando */
            messageData.suggestCMD = Indexer('array').extract(arrayOfCommands).value;

            /* Verifica se é um comando */
            if (messageData.isCmd === true) {
                /* Define a remoção do comando */
                const removeRegExp = new RegExp(`^${messageData.command}`, 'gi');

                /* Remove o prefix e comando */
                messageData.body = messageData.body.slice(1).replace(removeRegExp, '');
            }

            /* Define dois números da sorte */
            messageData.side = Indexer('numbers').randnum(1, 2).value;
            messageData.lvpc = Indexer('numbers').randnum(1, 100).value;

            /* Define o tipo de chat para argumento */
            messageData.typeChat = messageData.isGroupMsg ? 'groups' : 'private';

            /* Se não for a Íris */
            if (!messageData.isBot) {
                /* Adiciona 1 no contador de mensagens gerais */
                global.messagesCount[messageData.typeChat] += 1;

                /* Se for adiciona 1 no contador da Íris */
            } else global.messagesCount.bot += 1;

            /* Ajusta a contagem final */
            global.messagesCount.total = global.messagesCount.groups + global.messagesCount.private;
            global.messagesCount.overall = global.messagesCount.groups + global.messagesCount.bot + global.messagesCount.private;

            /* Define o tipo de nome a usar */
            messageData.typeName = messageData.isGroupMsg ? messageData.name : messageData.pushname;

            /* Define o tipo de ID sem sufixo a usar */
            messageData.typeId = (messageData.isGroupMsg ? messageData.chatId : messageData.user).replace(/@s.whatsapp.net|@g.us/gi, '');

            /* Escolhe um membro aleatório */
            messageData.randomMember = Indexer('arrays').extract(messageData.groupMembersId).value;

            /* Comando com uppercase na 1 letra */
            messageData.upperCommand = Indexer('string').upperland(messageData.command).value;

            /* Alias dos comandos */
            messageData.alias = envInfo.parameters.alias.value[commander] || [];

            /* JSONs na messageData */
            messageData.functions = functions;
            messageData.leveling = Leveling;
            messageData.bank = Bank;

            /* Define se é VIP */
            messageData.isAllowed = (
                /* VIP */
                (messageData.isVIP && commands.vipCommands.includes(messageData.cmd))

                /* MOD */
                || (messageData.isModerator && commands.moderatorCommands.includes(messageData.command))

                /* Dono */
                || messageData.isOwner

                /* Admin */
                || messageData.isGroupAdmins

                /* None */
                || false
            );

            /* Ajusta o administrador */
            messageData.isGroupAdmins = messageData.isAllowed;

            /* ----------------------- SECURITY ----------------------- */

            /* Definições para os sistemas de segurança, já usadas acima em outras formas */
            messageData.oldbody = message?.conversation || '';
            messageData.content = Indexer('others').findkey(recMessage, ['content'], ['string'], ['contextInfo', 'quotedMessage']).value || '';
            messageData.caption = Indexer('others').findkey(recMessage, ['caption'], ['string'], ['contextInfo', 'quotedMessage']).value || '';
            messageData.comment = Indexer('others').findkey(recMessage, ['comment'], ['string'], ['contextInfo', 'quotedMessage']).value || '';
            messageData.filename = Indexer('others').findkey(recMessage, ['filename'], ['string'], ['contextInfo', 'quotedMessage']).value || '';
            messageData.matchedText = Indexer('others').findkey(recMessage, ['matchedText'], ['string'], ['contextInfo', 'quotedMessage']).value || '';
            messageData.text = Indexer('others').findkey(recMessage, ['text'], ['string'], ['contextInfo', 'quotedMessage']).value || '';
            messageData.descriptionT = Indexer('others').findkey(recMessage, ['description'], ['string'], ['contextInfo', 'quotedMessage']).value || '';
            messageData.titleT = Indexer('others').findkey(recMessage, ['title'], ['string'], ['contextInfo', 'quotedMessage']).value || '';
            messageData.pollName = messageData.quoteThis.message?.pollCreationMessage?.name;
            messageData.pollOptions = messageData.quoteThis.message?.pollCreationMessage?.options?.map((all) => all.optionName);
            messageData.recMessage = recMessage;

            /* Se for uma marcação de mídia */
            if ((messageData.isCmd && messageData.quotedMsgObj?.mimetype && !messageData.isMedia) || (messageData.isCmd && isViewOnce)) {
                /* Define o conteúdo como a quotedMessage */
                let quotMessage = viewOnce || messageData.quotedMsg?.quotedMessage || quotedViewOnce;

                /* Se for uma quoted com ViewOnce */
                quotMessage = quotedViewOnce || quotMessage;

                /* Define o mimetype da quotedMessage */
                const myMimetype = quotMessage[Object.keys(quotMessage)[0]]?.mimetype || 'Not valid';

                /* Verifica se é algo válido para descriptografar */
                if (messageData.decryptFormats.includes(Object.keys(quotMessage)[0].replace('Message', '').toLowerCase())) {
                    /* Só baixa se não passar do peso limite (16MB padrão) */
                    if (((recMessage?.fileLength?.low || recMessage?.fileLength?.high || 1000) / 1024) <= config.maxDecryptSize.value) {
                        /* Baixa a mídia */
                        const demess = await downloadContentFromMessage(quotMessage[Object.keys(quotMessage)[0]], Object.keys(quotMessage)[0].replace('Message', ''));

                        /* Cria um buffer */
                        messageData.decryptedMedia = Buffer.from([]);

                        /* Faz um for await para unir os pedaços de buffer */
                        // eslint-disable-next-line no-restricted-syntax
                        for await (const chunk of demess) {
                            /* Adicionando eles como a mídia descriptograda */
                            messageData.decryptedMedia = Buffer.concat([messageData.decryptedMedia, chunk]);
                        }

                        /* Redefine variaveis para a mídia marcada */
                        /* eslint-disable-next-line no-nested-ternary */
                        messageData.encryptMedia = (isViewOnce && viewOnceObj) ? viewOnce[Object.keys(viewOnce)[0]] : (isViewOnce ? messageData.recMessage : messageData.quotedMsgObj);
                        messageData.isMedia = true;
                        messageData.mimetype = myMimetype || '';
                        messageData.isVideo = ['video/mp4', 'video', 'video/mpeg', 'video/quicktime', 'video/x-flv', 'video/x-ms-asf', 'video/avi', 'video/3gpp'].includes(myMimetype);
                        messageData.isImage = ['image', 'image/jpeg', 'image/bmp', 'image/png', 'image/webp'].includes(myMimetype);
                        messageData.canSticker = messageData.isImage || messageData.isVideo || false;
                    }
                }

                /* Define os tipos */
                messageData.quotedType = messageData.quotedMsg?.quotedMessage ? (Object.keys(messageData.quotedMsg?.quotedMessage)[0] || 'conversation') : 'conversation';
                messageData.quotedBaseMsg = messageData.quotedMsg?.quotedMessage || {};

                /* Ajusta a type se for ViewOnce marcada */
                if (quotedViewOnce) {
                    messageData.quotedType = quotedViewOnce ? (Object.keys(quotedViewOnce)[0] || 'conversation') : 'conversation';
                    messageData.quotedBaseMsg = quotedViewOnce || {};
                }

                /* Define o type formatado */
                messageData.quotedTypeFormated = messageData.quotedType.replace('Message', '');
                messageData.quotedTypeLower = messageData.quotedTypeFormated.toLowerCase();
            }

            /* --------------------- Sticker Info -------------------- */

            /* Define se é um sticker */
            messageData.isSticker = messageData.typeLower === 'sticker';

            /* Define se é um sticker mencionado */
            messageData.isQuotedSticker = messageData.quotedTypeLower === 'sticker';

            /* Define se é animado */
            messageData.isAnimated = recMessage?.isAnimated === true;

            /* Define se é um sticker mencionado animado */
            messageData.isQuotedAnimated = messageData.quotedMsgObj.isAnimated;

            /* Se for um sticker */
            if ((messageData.isSticker || messageData.isQuotedSticker) && messageData.decryptedMedia) {
                /* Extrai a metadata */
                messageData.stickerMetadata = await extractMetadata(messageData.decryptedMedia).catch(() => {
                    /* Redefine o stickerMetadata */
                    messageData.stickerMetadata = {
                        emojis: [],
                        'sticker-pack-id': '',
                        'sticker-pack-name': '',
                        'sticker-author-name': '',
                        'sticker-pack-publisher': '',
                        'android-app-store-link': '',
                        'ios-app-store-link': '',
                    };
                });
            }

            /* --------------------- Jogos --------------------------- */

            /* Define as patentes pelo idioma */
            messageData.allPatents = levelSettings.patentes[region];

            /* Adquire a patente do usuário */
            messageData.patente = Indexer('others').patent(Leveling.level, messageData.allPatents).value;

            /* Define o XP total para upar */
            messageData.requiredXP = Math.floor((Leveling.level + 1) * (levelSettings.base.value * levelSettings.multiplier.value));

            /* Calcula o novo nível */
            let newLevel = 0;
            let fullLevel = Leveling.level;

            /* Verifica se o XP atual é suficiente para avançar para o próximo nível */
            if (Leveling.xp >= messageData.requiredXP) {
                /* Calcula o novo nível com base no XP atual | 0.82 é o expoente mais seguro */
                newLevel = Math.floor(((Leveling.xp - messageData.requiredXP) / (levelSettings.base.value * levelSettings.multiplier.value)) ** 0.82);
                fullLevel += newLevel;

                /* Atualiza o XP necessário com base no novo nível */
                messageData.requiredXP = Math.floor((fullLevel + 1) * (levelSettings.base.value * levelSettings.multiplier.value));

                /* Se o XP atual ainda é suficiente */
                if (Leveling.xp >= messageData.requiredXP) {
                    /* Aumenta o level em +1 */
                    newLevel += 1;
                    fullLevel = Leveling.level + newLevel;

                    /* Ajusta novamente o XP necessário */
                    messageData.requiredXP = Math.floor((fullLevel + 1) * (levelSettings.base.value * levelSettings.multiplier.value));
                }
            }

            /* Define o nivel atual sem considerar o último upado */
            fullLevel -= 1;

            /* Define a base das imagens */
            messageData.profilePics = false;

            /* Define a Object de atualizar valores */
            const gainValues = {
                level: newLevel,
            };

            /* Define quanto vai ganhar de cada item */
            Object.keys(levelSettings.materials.value.wins).forEach((h) => {
                /* Salva na object o valor */
                gainValues[h] = (Math.floor(levelSettings.materials.value.wins[h] * (fullLevel * levelSettings.materials.value.multiply) - fullLevel)) || levelSettings.materials.value.wins[h];
            });

            /* Remove valores inválidos */
            Object.keys(gainValues).forEach((g) => {
                /* Como abaixo de zero ou Infinity */
                if (gainValues[g] < 0 || gainValues[g] === Infinity) {
                    /* Define o valor como 1 */
                    gainValues[g] = 1;

                    /* Se for level, ajusta o XP requisitado */
                    if (g === 'level') {
                        /* Ajusta novamente o XP necessário */
                        messageData.requiredXP = Math.floor((gainValues.level + 1) * (levelSettings.base.value * levelSettings.multiplier.value));
                    }
                }
            });
            messageData.winTaxes = gainValues;

            /* Verifica se foi level up */
            if (newLevel !== 0 && functions.leveling.enable) {
                /* Ganha valores */
                messageData.leveling = Indexer('sql').update('leveling', messageData.user, messageData.chatId, false, gainValues).value;

                /* Adquire a patente do usuário novamente */
                messageData.patente = Indexer('others').patent(messageData.leveling.level, messageData.allPatents).value;

                /* Se tiver o card ativo */
                if (levelSettings.card.value) {
                    /* Obtém a foto de perfil */
                    messageData.profilePics = await Indexer('profile').perfil(kill, {
                        value: {
                            quotedMsg: messageData.quotedMsg,
                            decryptedMedia: messageData.decryptedMedia,
                            mentionedJidList: messageData.mentionedJidList,
                            groupMembersId: messageData.groupMembersId,
                            canSticker: messageData.canSticker,
                            mimetype: messageData.mimetype,
                        },
                    }, true);
                    messageData.profilePics = messageData.profilePics.value;

                    /* Gera um card */
                    const cardLeveling = await Indexer('cards').up(messageData.profilePics[0], messageData.personal.name.text, Leveling.level, messageData.leveling.level);

                    /* Envia */
                    await kill.sendMessage(messageData.chatId, { image: cardLeveling.value, caption: `🎉 *LEVEL UP! @${messageData.userFormated}!* 🎉\n\n🎓 Patente: ${messageData.patente}\n📈 XP: ${messageData.leveling.xp}/${messageData.requiredXP}\n🎚️ Level: ${Leveling.level} ➔ ${messageData.leveling.level}\n📬 Messages: ${messageData.leveling.messages}\n💰 Coin: ${messageData.leveling.coin}\n💎 Diamond: ${messageData.leveling.diamond}\n🔴 Rubi: ${messageData.leveling.rubi}\n🪨 Stone: ${messageData.leveling.stone}\n🏆 Gold: ${messageData.leveling.gold}\n🪙 Iron: ${messageData.leveling.iron}\n🌲 Wood: ${messageData.leveling.wood}`, mentions: [messageData.user] });
                }
            }

            /* Adiciona a informação de grupo ativo na DB */
            Indexer('sql').custom('global', messageData.user, messageData.chatId, 'SELECT \'OK\';');

            /* Caso um dos parâmetros enviados não esteja OK */
        } else {
            /* Define os dados como a mensagem raiz (ainda causará erros, mas será muito melhor que null, false, etc */
            let recMessage = messageData.quoteThis.message[Object.keys(messageData.quoteThis.message)[0]];
            recMessage = recMessage?.message ? recMessage?.message[Object.keys(recMessage?.message)[0]] : recMessage;
            messageData = {
                ...messageData,
                ...recMessage,
            };
        }

        /* Define o resultado final */
        envInfo.results.value = messageData;

        /* 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);
}

/* Define a pasta correta */
function locateFolder(foldername) {
    /* Define o nome das pastas */
    let file = fs.readdirSync(envInfo.parameters.baseCMDs.value);

    /* Busca a pasta correta do arquivo SQL */
    file = file.filter((fd) => fd.toLowerCase() === foldername.toLowerCase())[0] || false;

    /* Retorna o encontrado */
    return file;
}

/* Faz a função para obter os comandos, sem envInfo */
function controlSystem(
    cmdTimes = envInfo.functions.control.arguments.cmdTimes.value,
) {
    /* Determina o resultado */
    let switcheroo = false;

    /* Define valor padrão */
    let commandName = locateFolder(cmdTimes) || cmdTimes || 'Default';

    /* Try pra caso o arquivo de função não exista */
    try {
        /* Define Default se for main */
        if (cmdTimes === 'main' || cmdTimes === 'Main') {
            /* Retorna 'default', indo para os comandos sem prefix */
            commandName = 'Default';
        }

        /* Determina a pasta de verificação por case */
        let commander = Object.keys(envInfo.parameters.alias.value).filter((cm) => envInfo.parameters.alias.value[cm].includes(commandName));

        /* Se ainda for vazio */
        if (commander.length === 0) {
            /* Obtém comandos similares */
            commander = Object.entries(envInfo.parameters.alias.value).reduce((result, [key, words]) => {
                /* Faz uma verificação de palavra a palavra */
                const similarWords = words.filter((f) => {
                    /* Similar -> 0-100 */
                    const similarity = issimilar(commandName, f) * 100;

                    /* Se ela for maior que x (70% padrão) */
                    return similarity >= config.minSimilarity.value;
                });

                /* Se houver palavras similares */
                if (similarWords.length > 0) {
                    /* Adiciona ao resultado a pasta do comando */
                    result.push(key);
                }

                /* Retorna o resultado */
                return result;
            }, []);
        }

        /* Define o comando */
        commander = commander[0] || 'Default';

        /* Verifica pelo commandName (Pelo nome da pasta) */
        if (fs.existsSync(`${envInfo.parameters.baseCMDs.value}${commandName}`)) {
            /* Define a pasta */
            switcheroo = `../../${commandName}`;

            /* Só edita se não existir no formato atual */
            if (envInfo.parameters.alias.value[commandName] == null) {
                /* Define o arquivo padrão para não enviar dados ruins */
                const defaultEnvir = JSON.parse(fs.readFileSync(`${__dirname}/utils.json`));

                /* Define a alias atual */
                const alliesBro = Object.keys(defaultEnvir.parameters.alias.value).filter((d) => d.toLowerCase() === commandName.toLowerCase())[0];

                /* Se existir um valor já, mas a pasta tenha sido só trocado pelo case insensitive */
                if (defaultEnvir.parameters.alias.value[alliesBro] != null && alliesBro !== commandName) {
                    /* Troca o valor */
                    defaultEnvir.parameters.alias.value[commandName] = defaultEnvir.parameters.alias.value[alliesBro];

                    /* Deleta o antigo */
                    delete defaultEnvir.parameters.alias.value[alliesBro];

                    /* Se nenhum caso, define automaticamente */
                } else {
                    /* Com base no commandName */
                    defaultEnvir.parameters.alias.value[commandName] = defaultEnvir.parameters.alias.value[commandName] || [commandName.toLowerCase()];
                }

                /* Salva no arquivo */
                fs.writeFileSync(`${__dirname}/utils.json`, JSON.stringify(defaultEnvir, null, 4));

                /* Redefine a envInfo */
                envInfo.functions.revert.value();
            }

            /* Verifica pelo commander (Alias) */
        } else if (fs.existsSync(`${envInfo.parameters.baseCMDs.value}${commander}`)) {
            /* Define a pasta */
            switcheroo = `../../${commander}`;

            /* Caso não tenha o comando */
        } else {
            /* Define como Default (Cases/No Prefix) */
            switcheroo = '../../Default';
        }

        /* Faz a exports e retorna ela */
        /* eslint-disable-next-line global-require, import/no-dynamic-require */
        const Sys = require(switcheroo);
        return Sys[Object.keys(Sys)[0]];

        /* Caso de algum erro */
    } catch (error) {
        /* Exibe o erro direto, essa parte não é da envInfo, até por que é sensivel */
        console.log(error);
    }

    /* Retorna um valor padrão se não funcionar acima */
    return false;
}

/**
 * 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.make]: { value: dataCollect },
        [envInfo.exports.control]: { value: controlSystem },
    },
    parameters: {
        location: { value: __filename },
    },
}, envFile, changeKey, dirname);
resetLocal();