Source: Commands/Mix/index.js

/* eslint-disable max-len */

/* Requires */
const fs = require('fs');
const Indexer = require('../../index');

/* JSON's | Utilidades */
const envInfo = JSON.parse(fs.readFileSync(`${__dirname}/utils.json`));
const onlineGames = {};
const playUsers = {};

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

/* Cria a função de comando */
async function guessGame(
    kill = envInfo.functions.exec.arguments.kill.value,
    env = envInfo.functions.exec.arguments.env.value,
) {
    /* Define um resultado padrão */
    envInfo.results.value = false;

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

    /* Try-Catch para casos de erro */
    try {
        /* Se recebeu tudo corretamente, se der ruim, não fará nada */
        if (typeof kill === 'object' && typeof env === 'object') {
            /* Define os dados necessarios */
            const {
                chatId,
                isOwner,
                reply,
                arks,
                body,
                command,
                isGroupMsg,
            } = env.value;

            /* Define o alias na envInfo */
            envInfo.alias = env.value.alias;

            /* Cria um objeto em memória se for um grupo */
            if (isGroupMsg) {
                /* Se ela já existir usa a que existe, se não, cria */
                onlineGames[chatId] = onlineGames[chatId] || {};

                /* Se for menu de ajuda de DEV */
                if (arks.includes('--help-dev') && isOwner === true) {
                    /* Manda a mensagem de ajuda de dev */
                    envInfo.results.value = await kill.sendMessage(chatId, {
                        text: Indexer('sql').languages(region, 'Helper', 'Developer', true, true, envInfo).value,
                    }, reply);

                    /* Menu de ajuda normal */
                } else if (arks.includes('--help')) {
                    /* Sem dados para DEVs e mais */
                    envInfo.results.value = await kill.sendMessage(chatId, {
                        text: Indexer('sql').languages(region, 'Helper', 'User', true, true, envInfo).value,
                    }, reply);

                    /* Se pedir placar */
                } else if (arks.includes('--placar')) {
                    /* Verifica se não tem jogos */
                    if (Object.keys(playUsers).length === 0) {
                        /* Diz que já pode criar */
                        await kill.sendMessage(chatId, { text: Indexer('sql').languages(region, 'Games', 'NoPlacar', true, true, env.value).value }, reply);

                        /* Se tiver */
                    } else {
                        /* Cria o placar */
                        const scoreboard = Object.keys(playUsers).sort((a, b) => playUsers[b].rank - playUsers[a].rank).map((player) => {
                            /* Define o número para marcar */
                            let playerNumber = Indexer('sql').get('personal', player, chatId);
                            playerNumber = playerNumber.value.name.text === 'default' ? player.replace(/@s.whatsapp.net/gi, '') : playerNumber.value.name.text;

                            /* Define e retorna a string */
                            return `👤 ${playerNumber}: ${playUsers[player].rank} 🏆`;
                        });

                        /* Avisa que apagou */
                        await kill.sendMessage(chatId, { text: Indexer('sql').languages(region, 'Games', 'Placar', true, true, { scoreboard: scoreboard.join('\n\n') }).value }, reply);
                    }

                    /* Se for uso normal */
                } else {
                    /* Obtém todos os tipos de palavras */
                    const listTypes = JSON.parse(Indexer('sql').keywords(region, false, false, false, 'SELECT json_group_array(DISTINCT type) FROM words').value);

                    /* Se for pedindo um guia */
                    if (arks.includes('--lists')) {
                        /* Envia a mensagem */
                        envInfo.results.value = await kill.sendMessage(chatId, { text: JSON.stringify(listTypes, null, 4) });

                        /* Retorna a envInfo sem detalhes */
                        return envInfo.results;
                    }

                    /* Define os parametros */
                    const guessConfig = {
                        list: (body.match(new RegExp(`(?:^|\\s+)-*(${listTypes.join('|')})\\b`, 'i')) || ['-NONE', 'NONE'])[1],
                        language: (body.match(/(?:^|\s+)-*(pt|ar|jp|en|fr|es|id|ms|hi|de|it|ru|lat)\b/i) || ['-pt', 'pt'])[1],
                        table: 'words',
                        random: true,
                        command: false,
                        pay: true,
                        style: 'mix',
                        time: envInfo.parameters.maxTime.value,
                        limit: Date.now() + envInfo.parameters.maxTime.value,
                    };

                    /* Se estiver usando guess customizado */
                    guessConfig.random = guessConfig.list === 'NONE';
                    guessConfig.list = guessConfig.random ? 'jobs' : guessConfig.list;

                    /* Define outras formas */
                    guessConfig.style = command === 'encoded' ? 'encoded' : guessConfig.style;
                    guessConfig.style = ['forca', 'hang', 'hangsman'].includes(command) ? 'forca' : guessConfig.style;
                    guessConfig.style = ['advinha', 'guess'].includes(command) ? 'guess' : guessConfig.style;
                    guessConfig.style = ['back', 'inverse'].includes(command) ? 'back' : guessConfig.style;

                    /* Define a palavra a usar */
                    const getKeyword = Indexer('sql').keywords(guessConfig.language, guessConfig.list, guessConfig.table, guessConfig.random, guessConfig.command);
                    const guessWord = {
                        ...getKeyword.value,
                    };

                    /* Define o tipo da palavra */
                    guessConfig.list = guessWord.type;

                    /* Define qual dos tipos de palavra usar */
                    guessConfig.wordUse = guessConfig.style === 'encoded' ? 'encoded' : 'mixed';
                    guessConfig.wordUse = guessConfig.style === 'forca' ? 'arrmistery' : guessConfig.wordUse;
                    guessConfig.wordUse = guessConfig.style === 'guess' ? 'mistery' : guessConfig.wordUse;
                    guessConfig.wordUse = guessConfig.style === 'back' ? 'reverse' : guessConfig.wordUse;

                    /* Salva em memória */
                    onlineGames[chatId][guessConfig.style] = onlineGames[chatId][guessConfig.style] || {};
                    onlineGames[chatId][guessConfig.style][guessWord.id] = { ...guessWord, ...guessConfig };

                    /* Se já estiver no limite */
                    if (Object.keys(onlineGames[chatId][guessConfig.style]).length > envInfo.parameters.maxGames.value) {
                        /* Define como sem pagamento */
                        guessConfig.pay = false;
                    }

                    /* Define nos dados para não pagar também */
                    guessWord.pay = guessConfig.pay;

                    /* Determina os ganhos com base no tamanho da palavra */
                    guessConfig.win = guessWord.pay ? Math.floor((guessWord.word.length * envInfo.parameters.winTax.value * (envInfo.parameters.typeWin.value[guessConfig.style] || 1)) + guessWord.word.length) : 0;

                    /* Envia a mensagem de novo jogo */
                    envInfo.results.value = await kill.sendMessage(chatId, {
                        text: Indexer('sql').languages(region, 'Games', 'wordStart', true, true, {
                            title: guessConfig.style.toUpperCase(),
                            time: `${Math.floor(guessConfig.time / 60000)}m`,
                            keyword: guessWord[guessConfig.wordUse].toUpperCase(),
                            wins: `${guessConfig.win} Í-Coins`,
                            type: guessConfig.list.toUpperCase(),
                            initial: guessWord.word.toUpperCase().slice(0, Math.max(Math.floor(guessWord[guessConfig.wordUse].length * 0.3), 1)),
                            letters: guessWord[guessConfig.wordUse].length,
                            spaces: (guessWord[guessConfig.wordUse].match(/\s/g) || []).length,
                            traces: (guessWord[guessConfig.wordUse].match(/-/g) || []).length,
                            idioma: guessConfig.language,
                        }).value,
                    });

                    /* Define a função do filtro e de vitória */
                    async function winGame(message) {
                        /* Define o texto para checar */
                        const guessText = message.body || message.arks || message.argl.join(' ') || '';

                        /* Define se o texto é a resposta */
                        if (guessText?.toLowerCase() === guessWord.word.toLowerCase()) {
                            /* Define a vitória */
                            playUsers[message.user] = playUsers[message.user] || {
                                amount: 0,
                                rank: 0,
                                cooldown: Date.now(),
                                item: 'coin',
                            };

                            /* Adiciona 1 ganho e insere a data atual */
                            playUsers[message.user].amount += 1;
                            playUsers[message.user].rank += 1;
                            playUsers[message.user].lastwin = Date.now();

                            /* Se foi três vezes já */
                            if (playUsers[message.user].amount > envInfo.parameters.maxWins.value) {
                                /* Define o dialogo de ganhos */
                                let dialogueLimit = `${Indexer('sql').languages(region, 'Typings', 'Winner', true, true, { winner: message.userFormated, prize: envInfo.parameters.cdType.value, win: guessConfig.win }).value}`;

                                /* Se for a primeira vez excedendo o limite, aplica o cooldown */
                                if (playUsers[message.user].cooldown < Date.now()) {
                                    /* Aplica o novo limite de data */
                                    playUsers[message.user].cooldown = Date.now() + envInfo.parameters.winCooldown.value;

                                    /* Insere o diálogo de primeira vez */
                                    dialogueLimit += `\n\n${Indexer('sql').languages(region, 'Games', 'gameLimit', true, true, { waittime: Indexer('number').format(playUsers[message.user].cooldown - Date.now()).overall }).value}`;

                                    /* Insere o contador normal */
                                } else dialogueLimit += `\n\n⌛ ~ '${Indexer('number').format(playUsers[message.user].cooldown - Date.now()).overall}' for coins...`;

                                /* Define o limite de ganhos */
                                guessConfig.win = Math.floor(guessConfig.win * envInfo.parameters.typeWin.value.cooldown);

                                /* Define o ganho como o válido */
                                playUsers[message.user].item = envInfo.parameters.cdType.value;

                                /* Manda a mensagem de vitória diferente */
                                envInfo.results.value = await kill.sendMessage(chatId, {
                                    text: dialogueLimit,
                                    mentions: [message.user],
                                }, message.reply);

                                /* Se for ganho normal */
                            } else {
                                /* Envia a mensagem de vitória */
                                envInfo.results.value = await kill.sendMessage(chatId, {
                                    text: Indexer('sql').languages(region, 'Games', 'winGame', true, true, { winGame: guessConfig.win }).value,
                                }, message.reply);
                            }

                            /* Se passou do prazo */
                            if (Date.now() > playUsers[message.user].cooldown && playUsers[message.user].amount > envInfo.parameters.maxWins.value) {
                                /* Reseta o contador de ganhos para voltar a ganhar coins */
                                playUsers[message.user].amount = 0;
                                playUsers[message.user].cooldown = 0;
                            }

                            /* Deleta o jogo atual */
                            delete onlineGames[chatId][guessConfig.style][guessWord.id];

                            /* Adiciona os ganhos */
                            envInfo.results.value = Indexer('sql').update('leveling', message.user, message.chatId, playUsers[message.user].item, guessConfig.win);

                            /* Encerra o jogo atual | STOP = Encerrar | Continuar = * (Anything) */
                            return 'STOP';
                        }

                        /* Se não, retorna para continuar */
                        return 'CONTINUE';
                    }

                    /* Aguarda pela resposta */
                    await kill.awaitMessages(chatId, winGame, guessConfig.time);
                }

                /* Se não for grupo */
            } else {
                /* Manda a mensagem só de grupos */
                envInfo.results.value = await kill.sendMessage(chatId, {
                    text: Indexer('sql').languages(region, 'Extras', 'OnlyGroups', true, true, envInfo).value,
                }, reply);
            }
        }

        /*
            Define o sucesso, se seu comando der erro isso jamais será chamado
            Então o success automaticamente será false em falhas
        */
        envInfo.results.success = true;

        /* Caso de algum erro */
    } catch (error) {
        /* Insere tudo na envInfo */
        logging.echoError(error, envInfo, __dirname);

        /* Avisa que deu erro, manda o erro e data ao sistema S.E.R (Send/Special Error Report) */
        /* Insira o name que você definiu na envInfo (name) onde pede abaixo */
        await kill.sendMessage(env.value.chatId, {
            text: Indexer('sql').languages(region, 'S.E.R', error, true, true, {
                command: 'MIX',
                time: (new Date()).toLocaleString(),
            }).value,
        }, env.value.reply);
    }

    /* Retorna os resultados */
    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.exec]: { value: guessGame },
    },
    parameters: {
        location: { value: __filename },
    },
}, envFile, changeKey, dirname);
resetLocal();