diff --git a/app.js b/app.js index 2641a4d..7a9560d 100644 --- a/app.js +++ b/app.js @@ -1,7 +1,7 @@ const fs = require('fs'); const path = require('path'); const { Collection, Client, GatewayIntentBits, Events } = require('discord.js'); -const { getRandomElementFromArray } = require('./constants'); +const { getRandomElementFromArray } = require('./utils/constants'); require('dotenv').config(); const enviroment = process.env.NODE_ENV; diff --git a/commands/price.js b/commands/price.js index 4bb690e..bed2539 100644 --- a/commands/price.js +++ b/commands/price.js @@ -1,71 +1,12 @@ const { SlashCommandSubcommandBuilder, hyperlink, bold, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js'); const puppeteer = require('puppeteer'); const jsdom = require('jsdom'); -const { responses } = require('../constants'); - -function truncateText(text, max) { - return text.substr(0, max - 1).trim() + (text.length > max ? '...' : ''); -} - -const countryData = { - 'ar': { - name: 'Argentina', - currency: 'ARS', - pages: [ - { - name: 'Mercado Libre (Argentina)', - searchUrl: 'https://listado.mercadolibre.com.ar/%S#D%5BA:%S', - productUrl: 'https://articulo.mercadolibre.com.ar%S', - selectors: { - container: 'div.andes-card.ui-search-result', - link: 'a.ui-search-link', - price: 'span.price-tag-fraction', - title: 'h2.ui-search-item__title.shops__item-title', - }, - }, - ], - }, - 'us': { - name: 'United States', - currency: 'USD', - pages: [ - { - name: 'Amazon (United States)', - searchUrl: 'https://www.amazon.com/s?k=%S', - productUrl: 'https://www.amazon.com%S', - selectors: { - container: 'div.s-card-container > div.a-section > div.sg-row', - link: 'h2.a-size-mini a.a-link-normal', - price: 'span.a-price span.a-offscreen', - title: 'h2.a-size-mini span.a-size-medium', - }, - }, - ], - }, - 'cl': { - name: 'Chile', - currency: 'CLP', - pages: [ - { - name: 'Falabella', - searchUrl: 'https://www.falabella.com/falabella-cl/search?Ntt=%S', - productUrl: 'https://www.falabella.com%S', - selectors: { - container: 'div.pod-4_GRID', - link: 'a', - price: 'div.prices span.copy10', - title: 'div.pod-details a.pod-link span > b.pod-subTitle', - }, - }, - ], - }, -}; +const { countryData, DISCORD_MESSAGE_LENGTH_LIMIT } = require('../utils/constants'); +const truncateText = require('../scripts/truncateText'); +const generateLocalizedResponses = require('../scripts/generateLocalizedResponses'); const pages = Object.values(countryData).map((country) => country.pages).flat(1); -const ELEMENTS_LIMIT = 3; -const DISCORD_MESSAGE_LENGTH_LIMIT = 2000; - module.exports = { data: new SlashCommandSubcommandBuilder() .setName('prices') @@ -113,6 +54,18 @@ module.exports = { }) .setMaxLength(200) .setAutocomplete(true), + ) + .addIntegerOption(option => option + .setName('limit') + .setNameLocalizations({ + 'es-ES': 'límite', + }) + .setDescription('Define the limit of results of search') + .setDescriptionLocalizations({ + 'es-ES': 'Definir el límite de resultados de la busqueda', + }) + .setMaxValue(5) + , ), async autocomplete(interaction) { const focusedValue = interaction.options.getFocused(); @@ -128,8 +81,9 @@ module.exports = { await interaction.deferReply(); const product = interaction.options.getString('product'); const platform = interaction.options.getString('platform'); + const ELEMENTS_LIMIT = interaction.options.getInteger('limit') || 3; if (platform && !pages.some(page => (page.name === platform))) { - await interaction.editReply(responses(userLanguage).missingPlatform); + await interaction.editReply(generateLocalizedResponses(userLanguage).missingPlatform); return; } const country = interaction.options.getString('country') || @@ -196,22 +150,25 @@ module.exports = { (new ActionRowBuilder() .addComponents( new ButtonBuilder() - .setLabel(responses(userLanguage).platformInBrowser.replace('%P', page.name)) + .setLabel(generateLocalizedResponses(userLanguage).platformInBrowser.replace('%P', page.name)) .setURL(page.searchUrl) .setStyle(ButtonStyle.Link), )), ); const replyTexts = [ pagesScraped.length && - `${responses(userLanguage).extractedFrom} ${pagesScraped.map(({ name }) => name).join(' ')}`, + `${generateLocalizedResponses(userLanguage).extractedFrom} ${pagesScraped.map(({ name }) => name).join(' ')}`, `${productPrices.join('\n')}`, pagesWithErrorScrapping.length && - `${responses(userLanguage).errorScrapping} ${pagesWithErrorScrapping.map((name) => name).join(' ')}`, + `${generateLocalizedResponses(userLanguage).errorScrapping} ${pagesWithErrorScrapping.map((name) => name).join(' ')}`, ].filter(a => a); const response = replyTexts.join('\n\n'); let content; if (response.length >= DISCORD_MESSAGE_LENGTH_LIMIT) { - content = responses(userLanguage).discordMessageLengthLimit; + content = generateLocalizedResponses(userLanguage).discordMessageLengthLimit; + } + else { + content = response; } await interaction.editReply({ content, components: [...buttons] }); }, diff --git a/commands/suggestCustomText.js b/commands/suggestCustomText.js index f8e539d..7f7f4fa 100644 --- a/commands/suggestCustomText.js +++ b/commands/suggestCustomText.js @@ -1,5 +1,6 @@ const { SlashCommandSubcommandBuilder } = require('discord.js'); -const { urlRegex, responses } = require('../constants'); +const generateLocalizedResponses = require('../scripts/generateLocalizedResponses'); +const { urlRegex } = require('../utils/constants'); require('dotenv').config(); module.exports = { @@ -26,16 +27,16 @@ module.exports = { const userLanguage = interaction.locale || 'en-US'; const suggestion = interaction.options.getString('text'); if (!suggestion) { - interaction.reply(responses(userLanguage).notSuggest); + interaction.reply(generateLocalizedResponses(userLanguage).notSuggest); return; } if (urlRegex.test(suggestion)) { - interaction.reply(responses(userLanguage).linksNotAllowed); + interaction.reply(generateLocalizedResponses(userLanguage).linksNotAllowed); return; } const notificationsChannel = interaction.client.channels.cache.get(process.env.NOTIFICATION_DISCORD_CHANNEL_ID); notificationsChannel.send(`SUGGESTION: ${suggestion}`); - await interaction.reply(responses(userLanguage).suggestionSended); + await interaction.reply(generateLocalizedResponses(userLanguage).suggestionSended); }, }; \ No newline at end of file diff --git a/commands/suggestPriceOption.js b/commands/suggestPriceOption.js index 7c07a0c..7ce6246 100644 --- a/commands/suggestPriceOption.js +++ b/commands/suggestPriceOption.js @@ -1,5 +1,6 @@ const { SlashCommandSubcommandBuilder } = require('discord.js'); -const { urlRegex, responses } = require('../constants'); +const generateLocalizedResponses = require('../scripts/generateLocalizedResponses'); +const { urlRegex } = require('../utils/constants'); require('dotenv').config(); module.exports = { @@ -37,15 +38,15 @@ module.exports = { const platform = interaction.options.getString('platform'); const suggestion = [country, platform].filter(a => a).join(' - '); if (!country && !platform) { - interaction.reply(responses(userLanguage).notSuggest); + interaction.reply(generateLocalizedResponses(userLanguage).notSuggest); return; } if (urlRegex.test(suggestion)) { - interaction.reply(responses(userLanguage).linksNotAllowed); + interaction.reply(generateLocalizedResponses(userLanguage).linksNotAllowed); return; } const notificationsChannel = interaction.client.channels.cache.get(process.env.NOTIFICATION_DISCORD_CHANNEL_ID); notificationsChannel.send(`SUGGESTION of country: ${suggestion}`); - await interaction.reply(responses(userLanguage).suggestionSended); + await interaction.reply(generateLocalizedResponses(userLanguage).suggestionSended); }, }; \ No newline at end of file diff --git a/constants.js b/constants.js deleted file mode 100644 index b5e5fff..0000000 --- a/constants.js +++ /dev/null @@ -1,54 +0,0 @@ -require('dotenv').config(); - -const urlRegex = /(((https?:\/\/)|(www\.))[^\s]+)/g; -function getRandomElementFromArray(arr) { - return arr[Math.floor((Math.random() * arr.length))]; -} -const responsesTexts = { - suggestionSended: { - 'en-US': `Suggestion sended to <@${process.env.MY_DISCORD_USER_ID}>`, - 'es-ES': `Sugerencia enviada a <@${process.env.MY_DISCORD_USER_ID}>`, - }, - extractedFrom: { - 'en-US': 'Prices extracted from:', - 'es-ES': 'Precios extraídos de:', - }, - missingPlatform: { - 'en-US': 'ERROR: Platform don\'t found!!', - 'es-ES': 'ERROR: Plataforma no encontrada!!', - }, - platformInBrowser: { - 'en-US': 'Search in %P on browser', - 'es-ES': 'Buscar en %P en el buscador', - }, - errorScrapping: { - 'en-US': 'No products could be found in:', - 'es-ES': 'No se pudieron encontrar productos en:', - }, - notSuggest: { - 'en-US': 'Please suggest someting :tired_face:', - 'es-ES': 'Por favor, sugiere algo :tired_face:', - }, - linksNotAllowed: { - 'en-US': 'Links aren\'t allowed :/', - 'es-ES': 'No esta permitido enviar links :/', - }, - discordMessageLengthLimit: { - 'en-US': 'Sorry, the links of this product exceeds the limit of characters by discord message.\n\nPlease try again with a lower quantity of results.', - 'es-ES': 'Lo sentimos, los enlaces de este producto exceden el límite de caracteres por mensaje de discord.\n\nPor favor, intente nuevamente con una menor cantidad de resultados.', - }, -}; -function responses(userLanguage) { - const responsesEntries = Object.entries(responsesTexts); - const localizatedResponses = {}; - responsesEntries.forEach(([responseName, responseTexts]) => { - localizatedResponses[responseName] = Object.entries(responseTexts).find(responseText => responseText[0] === userLanguage)[1]; - }); - return localizatedResponses; -} - -module.exports = { - urlRegex, - getRandomElementFromArray, - responses, -}; \ No newline at end of file diff --git a/scripts/generateLocalizedResponses.js b/scripts/generateLocalizedResponses.js new file mode 100644 index 0000000..9903030 --- /dev/null +++ b/scripts/generateLocalizedResponses.js @@ -0,0 +1,12 @@ +const { responsesTexts } = require('../utils/constants'); + +function generateLocalizedResponses(userLanguage) { + const responsesEntries = Object.entries(responsesTexts); + const localizatedResponses = {}; + responsesEntries.forEach(([responseName, responseTexts]) => { + localizatedResponses[responseName] = Object.entries(responseTexts).find(responseText => responseText[0] === userLanguage)[1]; + }); + return localizatedResponses; +} + +module.exports = generateLocalizedResponses; \ No newline at end of file diff --git a/scripts/getRandomElementFromArray.js b/scripts/getRandomElementFromArray.js new file mode 100644 index 0000000..2a6783b --- /dev/null +++ b/scripts/getRandomElementFromArray.js @@ -0,0 +1,5 @@ +function getRandomElementFromArray(arr) { + return arr[Math.floor((Math.random() * arr.length))]; +} + +module.exports = getRandomElementFromArray; \ No newline at end of file diff --git a/scripts/truncateText.js b/scripts/truncateText.js new file mode 100644 index 0000000..118c6f5 --- /dev/null +++ b/scripts/truncateText.js @@ -0,0 +1,5 @@ +function truncateText(text, max) { + return text.substr(0, max - 1).trim() + (text.length > max ? '...' : ''); +} + +module.exports = truncateText; \ No newline at end of file diff --git a/utils/constants.js b/utils/constants.js new file mode 100644 index 0000000..811d44d --- /dev/null +++ b/utils/constants.js @@ -0,0 +1,102 @@ +require('dotenv').config(); + +const countryData = { + 'ar': { + name: 'Argentina', + currency: 'ARS', + pages: [ + { + name: 'Mercado Libre (Argentina)', + searchUrl: 'https://listado.mercadolibre.com.ar/%S#D%5BA:%S', + productUrl: 'https://articulo.mercadolibre.com.ar%S', + selectors: { + container: 'div.andes-card.ui-search-result', + link: 'a.ui-search-link', + price: 'span.price-tag-fraction', + title: 'h2.ui-search-item__title.shops__item-title', + }, + }, + ], + }, + 'us': { + name: 'United States', + currency: 'USD', + pages: [ + { + name: 'Amazon (United States)', + searchUrl: 'https://www.amazon.com/s?k=%S', + productUrl: 'https://www.amazon.com%S', + selectors: { + container: 'div.s-card-container > div.a-section > div.sg-row', + link: 'h2.a-size-mini a.a-link-normal', + price: 'span.a-price span.a-offscreen', + title: 'h2.a-size-mini span.a-size-medium', + }, + }, + ], + }, + 'cl': { + name: 'Chile', + currency: 'CLP', + pages: [ + { + name: 'Falabella', + searchUrl: 'https://www.falabella.com/falabella-cl/search?Ntt=%S', + productUrl: 'https://www.falabella.com%S', + selectors: { + container: 'div.pod-4_GRID', + link: 'a', + price: 'div.prices span.copy10', + title: 'div.pod-details a.pod-link span > b.pod-subTitle', + }, + }, + ], + }, +}; + +const DISCORD_MESSAGE_LENGTH_LIMIT = 2000; + +const responsesTexts = { + suggestionSended: { + 'en-US': `Suggestion sended to <@${process.env.MY_DISCORD_USER_ID}>`, + 'es-ES': `Sugerencia enviada a <@${process.env.MY_DISCORD_USER_ID}>`, + }, + extractedFrom: { + 'en-US': 'Prices extracted from:', + 'es-ES': 'Precios extraídos de:', + }, + missingPlatform: { + 'en-US': 'ERROR: Platform don\'t found!!', + 'es-ES': 'ERROR: Plataforma no encontrada!!', + }, + platformInBrowser: { + 'en-US': 'Search in %P on browser', + 'es-ES': 'Buscar en %P en el buscador', + }, + errorScrapping: { + 'en-US': 'No products could be found in:', + 'es-ES': 'No se pudieron encontrar productos en:', + }, + notSuggest: { + 'en-US': 'Please suggest someting :tired_face:', + 'es-ES': 'Por favor, sugiere algo :tired_face:', + }, + linksNotAllowed: { + 'en-US': 'Links aren\'t allowed :/', + 'es-ES': 'No esta permitido enviar links :/', + }, + discordMessageLengthLimit: { + 'en-US': 'Sorry, the links of this product exceeds the limit of characters by discord message.\n\nPlease try again with a lower quantity of results.', + 'es-ES': 'Lo sentimos, los enlaces de este producto exceden el límite de caracteres por mensaje de discord.\n\nPor favor, intente nuevamente con una menor cantidad de resultados.', + }, +}; + +const urlRegex = /(((https?:\/\/)|(www\.))[^\s]+)/g; + + +module.exports = { + countryData, + responsesTexts, + DISCORD_MESSAGE_LENGTH_LIMIT, + urlRegex, +}; \ No newline at end of file