"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); 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 pages = Object.values(countryData).map((country) => country.pages).flat(1); const ELEMENTS_LIMIT = 3; const DISCORD_MESSAGE_LENGTH_LIMIT = 2000; exports.default = { data: new SlashCommandSubcommandBuilder() .setName('prices') .setNameLocalizations({ 'es-ES': 'precios', }) .setDescription('Get the prices of a product') .setDescriptionLocalizations({ 'es-ES': 'Consigue los precios de algún producto', }) .addStringOption(option => option .setName('product') .setNameLocalizations({ 'es-ES': 'producto', }) .setDescription('Product that you want to search') .setDescriptionLocalizations({ 'es-ES': 'Producto que quieres buscar', }) .setRequired(true) .setMaxLength(200)) .addStringOption(option => option .setName('country') .setNameLocalizations({ 'es-ES': 'país', }) .setDescription('Country where search the prices') .setDescriptionLocalizations({ 'es-ES': 'País en donde encontrar los precios', }) .addChoices(...Object.entries(countryData).map(([key, value]) => ({ name: value.name, value: key, })))) .addStringOption(option => option .setName('platform') .setNameLocalizations({ 'es-ES': 'plataforma', }) .setDescription('Specify a platform to search') .setDescriptionLocalizations({ 'es-ES': 'Especificar una plataforma para la busqueda', }) .setMaxLength(200) .setAutocomplete(true)), autocomplete(interaction) { return __awaiter(this, void 0, void 0, function* () { const focusedValue = interaction.options.getFocused(); const country = interaction.options.getString('country'); const choices = (country ? countryData[country].pages : pages).map(page => (page.name)); const filtered = choices.filter(choice => choice.startsWith(focusedValue)); yield interaction.respond(filtered.map(choice => ({ name: choice, value: choice }))); }); }, execute(interaction) { return __awaiter(this, void 0, void 0, function* () { const userLanguage = interaction.locale || 'en-US'; yield interaction.deferReply(); const product = interaction.options.getString('product'); const platform = interaction.options.getString('platform'); if (platform && !pages.some(page => (page.name === platform))) { yield interaction.editReply(responses(userLanguage).missingPlatform); return; } const country = interaction.options.getString('country') || (platform ? Object.entries(countryData) .map(([key, value]) => value.pages.some(page => page.name === platform) ? key : false) .filter(a => a)[0] : 'us'); const countryPages = countryData[country].pages.filter(countryPage => platform ? countryPage.name === platform : countryPage); const productPrices = []; const pagesScraped = []; const pagesWithErrorScrapping = []; for (const countryPage of countryPages) { try { const browser = yield puppeteer.launch({ args: ['--no-sandbox'], }); const page = yield browser.newPage(); const searchUrl = countryPage.searchUrl.replace('%S', product); const response = yield page.goto(searchUrl, { waitUntil: 'domcontentloaded' }); const body = yield response.text(); const { window: { document } } = new jsdom.JSDOM(body); const products = document.querySelectorAll(countryPage.selectors.container); if (!products.length) { throw Error(); } products .forEach((element) => { if (productPrices.length >= ELEMENTS_LIMIT) { return; } try { const productRelativePath = element .querySelector(countryPage.selectors.link) .getAttribute('href') .replace(/.*\/\/[^/]*/, ''); const productName = element.querySelector(countryPage.selectors.title).textContent; const link = hyperlink(truncateText(productName, 100), countryPage.productUrl.replace('%S', encodeURI(productRelativePath))); const priceNumber = element.querySelector(countryPage.selectors.price).textContent.replace('$', '').replace(' ', ''); const price = `${countryData[country].currency} ${bold(priceNumber)}`; productPrices.push(`${link} | ${price}`); } catch (err) { console.log(`FUCK ${countryPage.name} MAQUETATION`); console.error(err); } }); yield browser.close(); pagesScraped.push({ name: countryPage.name, searchUrl: encodeURI(searchUrl) }); } catch (err) { pagesWithErrorScrapping.push(countryPage.name); console.log(`FUCK ${countryPage.name}`); console.error(err); } } const buttons = pagesScraped.map(page => (new ActionRowBuilder() .addComponents(new ButtonBuilder() .setLabel(responses(userLanguage).platformInBrowser.replace('%P', page.name)) .setURL(page.searchUrl) .setStyle(ButtonStyle.Link)))); const replyTexts = [ pagesScraped.length && `${responses(userLanguage).extractedFrom} ${pagesScraped.map(({ name }) => name).join(' ')}`, `${productPrices.join('\n')}`, pagesWithErrorScrapping.length && `${responses(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; } yield interaction.editReply({ content, components: [...buttons] }); }); }, };