"use strict"; const Promise = require("bluebird"); const dotty = require("dotty"); const cheerio = require("cheerio"); const url = require("url"); const chalk = require("chalk"); const config = require("./config.json"); const bhttp = require("bhttp").session({ headers: { "user-agent": `barcodebot (problems: ${config.email})` } }); const parseFoodbook = require("./parse-foodbook"); function log(barcode, message) { console.log(`${chalk.cyan(`[${barcode}]`)} ${message}`); } function mapCurrency(abbreviation) { if (abbreviation === "USD") { return "$"; } else if (abbreviation === "EUR") { return "€"; } else { throw new Error(`Unrecognized currency: ${abbreviation}`); } } function cleanValue(value) { if(value != null) { return value.replace(/\n/g, " ").replace(/\s+/, " ").trim(); } } let barcodeGetters = [ getBarcode_coop, getBarcode_bolCom, getBarcode_buycott, getBarcode_foodBook, getBarcode_eanData, getBarcode_openFoodFacts, ]; module.exports = function getBarcode(barcode) { return Promise.try(() => { return Promise.reduce(barcodeGetters, (lastResult, getter) => { if (lastResult == null) { return getter(barcode); } else { return lastResult; } }, null); }).then((result) => { if (result != null) { return { ... result, image: (result.image !== "") ? result.image : null }; } else { return null; } }) }; function getBarcode_eanData(barcode) { return Promise.try(() => { log(barcode, "Trying EANData..."); return bhttp.get(`https://eandata.com/feed/?v=3&keycode=${config.apiKeys.eanData}&mode=json&find=${barcode}`); }).then((response) => { if (response.statusCode === 200) { let productName = dotty.get(response.body, "product.attributes.product"); if (productName != null) { let data = { name: cleanValue(productName), image: dotty.get(response.body, "product.image") }; let priceCurrency = dotty.get(response.body, "product.attributes.price_new_extra"); let priceAmount = dotty.get(response.body, "product.attributes.price_new"); if (priceAmount != null) { data.price = `${mapCurrency(priceCurrency)} ${priceAmount}`; } return data; } else { return null; } } else { return null; } }); } let coop404Regex = /We kunnen de pagina die je zoekt niet vinden/; function getBarcode_coop(barcode) { return Promise.try(() => { log(barcode, "Trying Coop..."); return bhttp.get(`https://www.coop.nl/inventory/product/${barcode}`); }).then((response) => { if (response.statusCode === 200 && !coop404Regex.test(response.body.toString())) { let $ = cheerio.load(response.body); let $imageNoscript = cheerio.load($("picture noscript").html()); return { name: cleanValue($(".detailHeader").text().trim().replace(/\n/g, " - ")), // image: $('meta[property="og:image"]').attr("content"), image: $imageNoscript('img').attr("src"), price: `€ ${$(".mainActions ins.price").text()}` }; } else { return null; } }); } function getBarcode_foodBook(barcode) { return Promise.try(() => { log(barcode, "Trying FoodBook..."); return bhttp.post(`https://foodbook.psinfoodservice.com/prod/dc`, { "SelectedFilterOptions.collapseFilterId": "filterkeywordPanelBody", "SelectedFilterOptions.FreeText": "", "SelectedFilterOptions.ProductGroupId": "", "SelectedFilterOptions.BrandId": "", "SelectedFilterOptions.GrossierId": "", "SelectedFilterOptions.Ean": barcode, "SelectedFilterOptions.ProducerNumber": "", "SelectedFilterOptions.GrossierNumber": "", "PS_brands_input": "", "PS_brands": "" }); }).then((response) => { let products = parseFoodbook.parseSearch(response.body); if (products.length > 0) { return Promise.try(() => { let productUrl = url.resolve("https://foodbook.psinfoodservice.com/prod/dc", products[0]); return bhttp.get(productUrl); }).then((response) => { if (response.statusCode === 200) { return parseFoodbook.parseProduct(response.body); } else { return null; } }); } else { return null; } }); } function getBarcode_openFoodFacts(barcode) { return Promise.try(() => { log(barcode, "Trying OpenFoodFacts..."); return bhttp.get(`https://world.openfoodfacts.org/api/v0/product/${encodeURIComponent(barcode)}.json`); }).then((response) => { if (response.statusCode === 200 && response.body.status === 1 && response.body.product.product_name != null) { return { name: cleanValue(response.body.product.product_name), image: response.body.product.image_url }; } else { return null; } }); } function getBarcode_buycott(barcode) { return Promise.try(() => { log(barcode, "Trying Buycott..."); return bhttp.get(`https://www.buycott.com/upc/${encodeURIComponent(barcode)}`); }).then((response) => { if (response.statusCode === 200) { let $ = cheerio.load(response.body); let product = cleanValue($("#container_header h2").text()); let tableData = $("table.product_info_table tr").get().map((row) => { let $row = $(row); return [ cleanValue($row.find("td:nth-child(1)").text()), cleanValue($row.find("td:nth-child(2)").text()), ]; }); let brandRow = tableData.find(([key, value]) => (key === "GS1 Name")); let productName; if (brandRow != null) { productName = `${brandRow[1]}, ${product}`; } else { productName = product; } return { name: productName, image: $(".header_image img").attr("src") }; } }); } function getBarcode_bolCom(barcode) { return Promise.try(() => { log(barcode, "Trying bol.com..."); return bhttp.get(`https://www.bol.com/nl/s/algemeen/zoekresultaten/Ntt/${encodeURIComponent(barcode)}/N/0/Nty/1/search/true/searchType/qck/defaultSearchContext/media_all/sc/media_all/index.html`); }).then((response) => { if (response.statusCode === 200) { let $ = cheerio.load(response.body); let productName = cleanValue($(".pdp-header__title").text()); if (productName.length > 0) { return { name: productName, image: $(".js_product_img").attr("src"), price: `€ ${cleanValue($(".promo-price").text())}` }; } } }); }